Эх сурвалжийг харах

#13170 favourites functions
合并LUMI分支,增加国际化支持

vbea 3 жил өмнө
parent
commit
346a477b8c
70 өөрчлөгдсөн 13867 нэмэгдсэн , 1037 устгасан
  1. 1 1
      Strides-APP/android/app/build.gradle
  2. BIN
      Strides-APP/android/app/src/main/res/drawable-xhdpi/ic_marker_star.png
  3. BIN
      Strides-APP/android/app/src/main/res/drawable-xhdpi/ic_marker_unstar.png
  4. BIN
      Strides-APP/android/app/src/main/res/drawable-xhdpi/ic_marker_upcoming.png
  5. 2 2
      Strides-APP/android/app/version.properties
  6. BIN
      Strides-APP/android/打包目录.lnk
  7. 13 4
      Strides-APP/app.json
  8. 3 0
      Strides-APP/app/api/apiCharge.js
  9. 7 0
      Strides-APP/app/api/apiStation.js
  10. 15 0
      Strides-APP/app/api/apiWallet.js
  11. 23 19
      Strides-APP/app/api/http.js
  12. 35 0
      Strides-APP/app/components/HeaderTitle.js
  13. 71 0
      Strides-APP/app/components/MyStatusBar.js
  14. 30 0
      Strides-APP/app/components/Switch.js
  15. 16 7
      Strides-APP/app/components/ThemesConfig.js
  16. 9083 0
      Strides-APP/app/components/countrysLocale.json
  17. 167 0
      Strides-APP/app/i18n/index.js
  18. 7 0
      Strides-APP/app/i18n/locales/de.js
  19. 417 0
      Strides-APP/app/i18n/locales/en.js
  20. 7 0
      Strides-APP/app/i18n/locales/es.js
  21. 7 0
      Strides-APP/app/i18n/locales/fr.js
  22. 7 0
      Strides-APP/app/i18n/locales/ja.js
  23. 7 0
      Strides-APP/app/i18n/locales/km.js
  24. 7 0
      Strides-APP/app/i18n/locales/ko.js
  25. 7 0
      Strides-APP/app/i18n/locales/my.js
  26. 7 0
      Strides-APP/app/i18n/locales/th.js
  27. 7 0
      Strides-APP/app/i18n/locales/vi.js
  28. 417 0
      Strides-APP/app/i18n/locales/zh-TW.js
  29. 417 0
      Strides-APP/app/i18n/locales/zh.js
  30. BIN
      Strides-APP/app/images/maps/ic_marker_star.png
  31. BIN
      Strides-APP/app/images/maps/ic_marker_unstar.png
  32. BIN
      Strides-APP/app/images/maps/ic_marker_upcoming.png
  33. BIN
      Strides-APP/app/images/site/charging-status-auth.png
  34. BIN
      Strides-APP/app/images/site/charging-status-charge.png
  35. BIN
      Strides-APP/app/images/site/charging-status-ready.png
  36. BIN
      Strides-APP/app/images/site/charging-status-unknow.png
  37. 7 6
      Strides-APP/app/pages/About.js
  38. 71 16
      Strides-APP/app/pages/Router.js
  39. 103 42
      Strides-APP/app/pages/Settings.js
  40. 266 0
      Strides-APP/app/pages/bookmark/Bookmarks.js
  41. 15 15
      Strides-APP/app/pages/charge/Details.js
  42. 49 27
      Strides-APP/app/pages/charge/QRScan.js
  43. 8 8
      Strides-APP/app/pages/charge/Summary.js
  44. 19 11
      Strides-APP/app/pages/chargeV2/ChargeAdapter.js
  45. 42 8
      Strides-APP/app/pages/chargeV2/PagerUtil.js
  46. 34 26
      Strides-APP/app/pages/chargeV2/Summary.js
  47. 245 498
      Strides-APP/app/pages/chargeV2/TabCharge.js
  48. 8 8
      Strides-APP/app/pages/chargeV2/TabInfos.js
  49. 31 31
      Strides-APP/app/pages/chargeV2/TabReserve.js
  50. 96 0
      Strides-APP/app/pages/charging/StationInfoView.js
  51. 117 0
      Strides-APP/app/pages/charging/StepChargeView.js
  52. 165 0
      Strides-APP/app/pages/charging/StepStartView.js
  53. 335 0
      Strides-APP/app/pages/chargingV2/ChargingPage.js
  54. 125 0
      Strides-APP/app/pages/chargingV2/StepAuth.js
  55. 285 0
      Strides-APP/app/pages/chargingV2/StepCharging.js
  56. 151 0
      Strides-APP/app/pages/chargingV2/StepStart.js
  57. 101 0
      Strides-APP/app/pages/chargingV2/StepStop.js
  58. 54 19
      Strides-APP/app/pages/home/Drawer.js
  59. 193 136
      Strides-APP/app/pages/home/Home.js
  60. 42 13
      Strides-APP/app/pages/home/maps/BottomSiteInfo.js
  61. 16 8
      Strides-APP/app/pages/home/maps/Cluster.js
  62. 145 0
      Strides-APP/app/pages/home/maps/LocationPermission.js
  63. 129 0
      Strides-APP/app/pages/payment/PaymentConfig.js
  64. 65 27
      Strides-APP/app/pages/search/ListViewV2.js
  65. 35 10
      Strides-APP/app/pages/search/SearchV2.js
  66. 1 1
      Strides-APP/app/pages/wallet/Wallet.js
  67. 66 8
      Strides-APP/app/utils/utils.js
  68. 1 1
      Strides-APP/app/utils/vector_icon.js
  69. 66 85
      Strides-APP/index.js
  70. 1 0
      Strides-APP/package.json

+ 1 - 1
Strides-APP/android/app/build.gradle

@@ -3,7 +3,7 @@ apply plugin: 'com.google.gms.google-services'
 
 import com.android.build.OutputFile
 
-def myVersionName = "2.3.1" //★★★★★版本号★★★★★
+def myVersionName = "2.4.0" //★★★★★版本号★★★★★
 /**
  * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
  * and bundleReleaseJsAndAssets).

BIN
Strides-APP/android/app/src/main/res/drawable-xhdpi/ic_marker_star.png


BIN
Strides-APP/android/app/src/main/res/drawable-xhdpi/ic_marker_unstar.png


BIN
Strides-APP/android/app/src/main/res/drawable-xhdpi/ic_marker_upcoming.png


+ 2 - 2
Strides-APP/android/app/version.properties

@@ -1,2 +1,2 @@
-#Tue Jun 13 10:35:34 CST 2023
-VERSION_CODE=228
+#Thu Jun 29 18:05:08 CST 2023
+VERSION_CODE=244

BIN
Strides-APP/android/打包目录.lnk


+ 13 - 4
Strides-APP/app.json

@@ -1,8 +1,17 @@
 {
   "name": "JuicePlus",
   "displayName": "ChargEco",
-  "versionCode": 230,
-  "versionName": "V2.3.1",
-  "product": true,
-  "debug": true
+  "versionCode": 240,
+  "versionName": "V2.4.0",
+  "product": false,
+  "debug": true,
+  "modules": {
+    "i18n": false,
+    "bookmarks": true,
+    "notifications": false
+  },
+  "storeUrl": {
+    "android": "market://details?id=com.strides.chargeco",
+    "ios": "itms-appss://apps.apple.com/app/id1664718768"
+  }
 }

+ 3 - 0
Strides-APP/app/api/apiCharge.js

@@ -42,5 +42,8 @@ export default {
   //取消预定
   cancelReserve: (reservePk) => {
     return get(prefix + 'cancelReserve', { reservePk: reservePk });
+  },
+  getConnectorDetail: (params) => {
+    return get(prefix + 'getConnectorUsageDetail', params);
   }
 }

+ 7 - 0
Strides-APP/app/api/apiStation.js

@@ -1,6 +1,7 @@
 import { get, post } from "./http";
 
 const prefix = 'devicesApi/site/';
+const userPrefix = 'devicesApi/user/sites/';
 
 export default {
   getAllStation: (params) => {
@@ -14,5 +15,11 @@ export default {
   },
   getNearestSite: (params) => {
     return get(prefix + 'getNearestSite', params);
+  },
+  getBookmarkList: (params) => {
+    return get(userPrefix + 'bookmarks', params);
+  },
+  bookmarkSite: (id) => {
+    return get(userPrefix + 'favorite', {sitePk: id});
   }
 }

+ 15 - 0
Strides-APP/app/api/apiWallet.js

@@ -6,6 +6,21 @@ export default wallet = {
   getTopUpAmountList: () => {
     return get(prefix + 'getTopUpAmountList', {});
   },
+  getTopUpAmountListV2: () => {
+    return get('devicesApi/purchase/top-up-amounts', {});
+  },
+  getTempAmountListV2: () => {
+    return [{
+      "currency": "CNY",
+      "amount": 20.00
+    }, {
+      "currency": "CNY",
+      "amount": 50.00
+    }, {
+      "currency": "CNY",
+      "amount": 100.00
+    }]
+  },
   addCreditCard: (params) => {
     return post(prefix + 'addCard', params);
   },

+ 23 - 19
Strides-APP/app/api/http.js

@@ -17,11 +17,15 @@ Axios.interceptors.response.use((response) => {
     console.log('-------', JSON.stringify(response.data));
     console.log('-------', response.status);
   }
-  if (response.data.code == '401') {
+  if (response.data.code == '401' || response.data.code == '402') {
     setAccessToken('');
-    startPage(PageList.login, {action: '401'});
+    startPage(PageList.login, {action: response.data.code});
     return Promise.reject('Need sign in');
   }
+  if (response.data.code == '500' || response.data.code == '502') {
+    setAccessToken('');
+    return Promise.reject('Sever error');
+  }
   return response.data;
 }, (error) => {
   console.info('-------error', error);
@@ -34,21 +38,21 @@ export const get = (path, params) => {
       params: params,
       method: 'GET',
       headers: {
-        'lang': 'SG',
         'Accept': 'application/json',
+        'lang': global.currentLocale,
         'accessToken': global.accessToken ?? ''
       }
     }).then(res => {
       if (res.success) {
         resolve(res);
       } else if (res.msg) {
-        reject(res.msg);
+        reject({err: res.msg, ...res});
       } else {
         reject('Request Failed');
       }
-    }).catch(err => {
-      console.info('HTTP-ERROR', err);
-      reject(err);
+    }).catch(error => {
+      console.info('HTTP-ERROR', error);
+      reject(error);
     });
   });
 }
@@ -58,21 +62,21 @@ export const post = (path, params) => {
     Axios.post(host + service + path, params, {
       method: 'POST',
       headers: {
-        'lang': 'SG',
         'Accept': 'application/json',
+        'lang': global.currentLocale,
         'accessToken': global.accessToken ?? ''
       }
     }).then(res => {
       if (res.success) {
         resolve(res);
       } else if (res.msg) {
-        reject(res.msg);
+        reject({err: res.msg, ...res});
       } else {
         reject('Request Failed');
       }
-    }).catch(err => {
-      console.info('HTTP-ERROR', err);
-      reject(err);
+    }).catch(error => {
+      console.info('HTTP-ERROR', error);
+      reject(error);
     });
   });
 }
@@ -82,7 +86,7 @@ export const upload = (path, params, header) => {
     Axios.post(host + service + path, params, {
       method: 'POST',
       headers: {
-        'lang': 'SG',
+        'lang': global.currentLocale,
         'Accept': 'application/json',
         'Content-Type': 'multipart/form-data',
         'accessToken': global.accessToken ?? '',
@@ -92,13 +96,13 @@ export const upload = (path, params, header) => {
       if (res.success) {
         resolve(res);
       } else if (res.msg) {
-        reject(res.msg);
+        reject({err: res.msg, ...res});
       } else {
         reject('Request Failed');
       }
-    }).catch(err => {
-      console.info('HTTP-ERROR', err);
-      reject(err);
+    }).catch(error => {
+      console.info('HTTP-ERROR', error);
+      reject(error);
     });
   })
 }
@@ -121,7 +125,7 @@ export const GET = (url, params) => {
     fetch(request, {
       method: 'GET',
       headers: {
-        'lang': 'SG',
+        'lang': global.currentLocale,
         'Accept': 'application/json',
         'accessToken': global.accessToken ?? ''
       }.then((response) => {
@@ -144,7 +148,7 @@ export const POST = (url, params) => {
     fetch(host + service + url, {
       method: 'POST',
       headers: {
-        'lang': 'SG',
+        'lang': global.currentLocale,
         'Accept': 'application/json',
         'content-type': 'application/json',
         'accessToken': global.accessToken ?? ''

+ 35 - 0
Strides-APP/app/components/HeaderTitle.js

@@ -0,0 +1,35 @@
+import React from 'react';
+import { Platform, StyleSheet, Text } from 'react-native';
+
+const HeaderTitle = ({scope, style=styles.titleColor}) => (
+  <Text
+    ariaLevel="1"
+    numberOfLines={1}
+    accessibilityRole="header"
+    style={[styles.title, style]}>
+    {$t(scope)}
+  </Text>
+);
+
+export default HeaderTitle;
+
+const styles = StyleSheet.create({
+  title: Platform.select({
+    ios: {
+      fontSize: 17,
+      fontWeight: '600'
+    },
+    android: {
+      fontSize: 20,
+      fontFamily: 'sans-serif-medium',
+      fontWeight: 'normal'
+    },
+    default: {
+      fontSize: 18,
+      fontWeight: '500'
+    }
+  }),
+  titleColor: {
+    color: pageTitleTint
+  }
+})

+ 71 - 0
Strides-APP/app/components/MyStatusBar.js

@@ -0,0 +1,71 @@
+/**
+ * 自定义状态栏(Android)
+ * @邠心vbe on 2023/02/15
+ */
+import React, { Component } from 'react';
+import { StatusBar } from 'react-native';
+
+let statusBar;
+export default class MyStatusBar extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      statusTheme: themeStatusBar,
+      statusColor: colorPrimaryDark
+    };
+    statusBar = this;
+  }
+
+  static DARK_STYLE = "dark-content";
+  static LIGHT_STYLE = "light-content";
+
+  /**
+   * 设置状态栏颜色
+   * @param {String} color 颜色值
+   */
+  static setStatusBarColor(color) {
+    statusBar.setStatusBarColor(color);
+  }
+
+  /**
+   * 设置状态栏主题(字体图标颜色)
+   * @param {String} theme 主题:MyStatusBar.DARK_STYLE, MyStatusBar.LIGHT_STYLE
+   */
+  static setStatusBarTheme(theme) {
+    statusBar.setStatusBarTheme(theme);
+  }
+
+  /**
+   * 设置状态栏主题和颜色
+   * @param {*} theme 主题:MyStatusBar.DARK_STYLE, MyStatusBar.LIGHT_STYLE
+   * @param {*} color 颜色
+   */
+  static setStatusBarThemes(theme, color) {
+    statusBar.setStatusBarThemes(theme, color);
+  }
+
+  setStatusBarColor(color) {
+    this.setState({
+      statusColor: color
+    })
+  }
+
+  setStatusBarTheme(theme) {
+    this.setState({
+      statusTheme: theme
+    })
+  }
+
+  setStatusBarThemes(theme, color) {
+    this.setState({
+      statusColor: color,
+      statusTheme: theme
+    })
+  }
+
+  render() {
+    return (
+      <StatusBar barStyle={this.state.statusTheme} backgroundColor={this.state.statusColor}/>
+    );
+  }
+}

+ 30 - 0
Strides-APP/app/components/Switch.js

@@ -0,0 +1,30 @@
+import React from 'react';
+import { Switch, SwitchProps, Text, View } from 'react-native';
+import Animated from 'react-native-reanimated';
+import utils from '../utils/utils';
+
+//const switchStyle = { false: "#B2B2B2", true: colorAccent };
+
+export default SwitchBase = (props, {...SwitchProps}) => {
+  const colors = utils.hexColorToRgb(colorAccent);
+  var thumbColor = null;
+  var trackColor = { false: "#B2B2B2", true: colorAccent }
+  if (!isIOS) {
+    thumbColor = (props.value ? utils.getRgbaColor(colors, 0.9) : "#EBEDEC")
+    trackColor = {false: "#B2B2B2", true: utils.getRgbaColor(colors, 0.3)}
+  }
+  return (
+    <Animated.View style={ui.flexc}>
+      <Switch
+        {...props}
+        trackColor={trackColor}
+        thumbColor={thumbColor}
+      />
+      {/* <Switch
+        {...props}
+      />
+      <Text style={{color: utils.getRgbaColor(colors, 0.9)}}>■■■</Text>
+      <Text style={{color: utils.getRgbaColor(colors, 0.3)}}>■■■</Text> */}
+    </Animated.View>
+  );
+}

+ 16 - 7
Strides-APP/app/components/ThemesConfig.js

@@ -1,13 +1,22 @@
-export const MyRefreshProps = {
-  title: 'Pulldown to Refresh',
+import React from "react";
+import { View } from "react-native";
+
+export const MyRefreshProps = () => ({
+  title: global.$t('common.pulldown2Refresh'),
   titleColor: textCancel,
   colors: [colorAccent, colorPrimary],
   tintColor: colorAccent
-}
+})
 
 export const UploadThemes = {
-  cropperStatusBarColor: colorLight,
-  cropperToolbarColor: colorLight,
-  cropperToolbarWidgetColor: textDark,
-  cropperActiveWidgetColor: colorAccent
+  cropperStatusBarColor: colorPrimary,
+  cropperToolbarColor: colorPrimary,
+  cropperActiveWidgetColor: colorAccent,
+  cropperToolbarWidgetColor: pageTitleTint
+}
+
+export const TestThemes = () => {
+  return (
+    <View/>
+  )
 }

+ 9083 - 0
Strides-APP/app/components/countrysLocale.json

@@ -0,0 +1,9083 @@
+[
+  {
+    "callingCode": [
+      "93"
+    ],
+    "countryCode": "AF",
+    "countryName": "Afghanistan",
+    "countryNames": {
+      "cs": "Afghánistán",
+      "cy": "Affganistan",
+      "de": "Afghanistan",
+      "en": "Afghanistan",
+      "es": "Afganistán",
+      "et": "Afganistan",
+      "fi": "Afganistan",
+      "fr": "Afghanistan",
+      "hr": "Afganistan",
+      "it": "Afghanistan",
+      "ja": "アフガニスタン",
+      "km": "Afghanistan",
+      "ko": "아 프가니스탄",
+      "my": "Afghanistan",
+      "nl": "Afghanistan",
+      "pl": "Afganistan",
+      "pt": "Afeganistão",
+      "ru": "Афганист ан",
+      "sk": "Afganistan",
+      "th": "อัฟกานิสถาน",
+      "ur": "افغانستان",
+      "vi": "Afghanistan",
+      "zh": "阿富汗"
+    },
+    "currency": [
+      "AFN"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "355"
+    ],
+    "countryCode": "AL",
+    "countryName": "Albania",
+    "countryNames": {
+      "cs": "Albánie",
+      "cy": "Albania",
+      "de": "Albanien",
+      "en": "Albania",
+      "es": "Albania",
+      "et": "Albaania",
+      "fi": "Albania",
+      "fr": "Albanie",
+      "hr": "Albanija",
+      "it": "Albania",
+      "ja": "アルバニア",
+      "km": "Albania",
+      "ko": "알바니아",
+      "my": "Albania",
+      "nl": "Albanië",
+      "pl": "Albania",
+      "pt": "Albânia",
+      "ru": "Албания",
+      "sk": "Albánsko",
+      "th": "แอลเบเนีย",
+      "ur": "البانیا",
+      "vi": "Albania",
+      "zh": "阿尔巴尼亚"
+    },
+    "currency": [
+      "ALL"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "213"
+    ],
+    "countryCode": "DZ",
+    "countryName": "Algeria",
+    "countryNames": {
+      "cs": "Alžírsko",
+      "cy": "Algeria",
+      "de": "Algerien",
+      "en": "Algeria",
+      "es": "Argelia",
+      "et": "Alžeeria",
+      "fi": "Algeria",
+      "fr": "Algérie",
+      "hr": "Alžir",
+      "it": "Algeria",
+      "ja": "アルジェリア",
+      "km": "Algeria",
+      "ko": "알제리",
+      "my": "Algeria",
+      "nl": "Algerije",
+      "pl": "Algieria",
+      "pt": "Argélia",
+      "ru": "Алжир",
+      "sk": "Alžírsko",
+      "th": "แอลจีเรีย",
+      "ur": "الجزائر",
+      "vi": "Algeria",
+      "zh": "阿尔及利亚"
+    },
+    "currency": [
+      "DZD"
+    ],
+    "region": "Africa",
+    "subregion": "Northern Africa"
+  },
+  {
+    "callingCode": [
+      "1684"
+    ],
+    "countryCode": "AS",
+    "countryName": "American Samoa",
+    "countryNames": {
+      "cs": "Americká Samoa",
+      "cy": "American Samoa",
+      "de": "Amerikanisch-Samoa",
+      "en": "American Samoa",
+      "es": "Samoa Americana",
+      "et": "Ameerika Samoa",
+      "fi": "Amerikan Samoa",
+      "fr": "Samoa américaines",
+      "hr": "Američka Samoa",
+      "it": "Samoa Americane",
+      "ja": "アメ リカ領サモア",
+      "km": "American Samoa",
+      "ko": "아메리칸사모아",
+      "my": "American Samoa",
+      "nl": "Amerikaans Samoa",
+      "pl": "Samoa Amerykańskie",
+      "pt": "Samoa Americana",
+      "ru": "Американское Самоа",
+      "sk": "Americká Samoa",
+      "th": "อเมริกันซามัว",
+      "ur": "امریکی سمووا",
+      "vi": "Samoa thuộc Mỹ",
+      "zh": "美属萨摩亚"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "376"
+    ],
+    "countryCode": "AD",
+    "countryName": "Andorra",
+    "countryNames": {
+      "cs": "Andorra",
+      "cy": "Andorra",
+      "de": "Andorra",
+      "en": "Andorra",
+      "es": "Andorra",
+      "et": "Andorra",
+      "fi": "Andorra",
+      "fr": "Andorre",
+      "hr": "Andora",
+      "it": "Andorra",
+      "ja": "アンドラ",
+      "km": "Andorra",
+      "ko": "안도라",
+      "my": "Andorra",
+      "nl": "Andorra",
+      "pl": "Andora",
+      "pt": "Andorra",
+      "ru": "Андорра",
+      "sk": "Andorra",
+      "th": "อันดอร์รา",
+      "ur": "انڈورا",
+      "vi": "Andorra",
+      "zh": "安道尔"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "244"
+    ],
+    "countryCode": "AO",
+    "countryName": "Angola",
+    "countryNames": {
+      "cs": "Angola",
+      "cy": "Angola",
+      "de": "Angola",
+      "en": "Angola",
+      "es": "Angola",
+      "et": "Angola",
+      "fi": "Angola",
+      "fr": "Angola",
+      "hr": "Angola",
+      "it": "Angola",
+      "ja": "アンゴラ",
+      "km": "Angola",
+      "ko": "앙골라",
+      "my": "Angola",
+      "nl": "Angola",
+      "pl": "Angola",
+      "pt": "Angola",
+      "ru": "Ан гола",
+      "sk": "Angola",
+      "th": "แองโกลา",
+      "ur": "انگولہ",
+      "vi": "Angola",
+      "zh": "安哥拉"
+    },
+    "currency": [
+      "AOA"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "1264"
+    ],
+    "countryCode": "AI",
+    "countryName": "Anguilla",
+    "countryNames": {
+      "cs": "Anguilla",
+      "cy": "Anguilla",
+      "de": "Anguilla",
+      "en": "Anguilla",
+      "es": "Anguilla",
+      "et": "Anguilla",
+      "fi": "Anguilla",
+      "fr": "Anguilla",
+      "hr": "Angvila",
+      "it": "Anguilla",
+      "ja": "アンギラ",
+      "km": "Anguilla",
+      "ko": "앵 귈라",
+      "my": "Anguilla",
+      "nl": "Anguilla",
+      "pl": "Anguilla",
+      "pt": "Anguilla",
+      "ru": "Ангилья",
+      "sk": "Anguilla",
+      "th": "แองกวิลลา",
+      "ur": "اینگویلا",
+      "vi": "Anguilla",
+      "zh": "安圭拉"
+    },
+    "currency": [
+      "XCD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "1268"
+    ],
+    "countryCode": "AG",
+    "countryName": "Antigua and Barbuda",
+    "countryNames": {
+      "cs": "Antigua a Barbuda",
+      "cy": "Antigwa a Barbiwda",
+      "de": "Antigua und Barbuda",
+      "en": "Antigua and Barbuda",
+      "es": "Antigua y Barbuda",
+      "et": "Antigua ja Barbuda",
+      "fi": "Antigua ja Barbuda",
+      "fr": "Antigua-et-Barbuda",
+      "hr": "Antigva i Barbuda",
+      "it": "Antigua e Barbuda",
+      "ja": "アンティグア・バーブーダ",
+      "km": "Antigua and Barbuda",
+      "ko": "앤티가 바부다",
+      "my": "Antigua and Barbuda",
+      "nl": "Antigua en Barbuda",
+      "pl": "Antigua i Barbuda",
+      "pt": "Antígua e Barbuda",
+      "ru": "Ан тигуа и Барбуда",
+      "sk": "Antigua a Barbuda",
+      "th": "แอนติกาและบาร์บูดา",
+      "ur": "اینٹیگوا و باربوڈا",
+      "vi": "Antigua và Barbuda",
+      "zh": "安提瓜和巴布达"
+    },
+    "currency": [
+      "XCD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "54"
+    ],
+    "countryCode": "AR",
+    "countryName": "Argentina",
+    "countryNames": {
+      "cs": "Argentina",
+      "cy": "Ariannin",
+      "de": "Argentinien",
+      "en": "Argentina",
+      "es": "Argentina",
+      "et": "Argentina",
+      "fi": "Argentiina",
+      "fr": "Argentine",
+      "hr": "Argentina",
+      "it": "Argentina",
+      "ja": "アルゼンチン",
+      "km": "Argentina",
+      "ko": "아르헨티나",
+      "my": "Argentina",
+      "nl": "Argentinië",
+      "pl": "Argentyna",
+      "pt": "Argentina",
+      "ru": "Аргентина",
+      "sk": "Argentína",
+      "th": "อาร์เจนตินา",
+      "ur": "ارجنٹائن",
+      "vi": "argentina",
+      "zh": "阿根廷"
+    },
+    "currency": [
+      "ARS"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "374"
+    ],
+    "countryCode": "AM",
+    "countryName": "Armenia",
+    "countryNames": {
+      "cs": "Arménie",
+      "cy": "Armenia",
+      "de": "Armenien",
+      "en": "Armenia",
+      "es": "Armenia",
+      "et": "Armeenia",
+      "fi": "Armenia",
+      "fr": "Arménie",
+      "hr": "Armenija",
+      "it": "Armenia",
+      "ja": "アル メニア",
+      "km": "Armenia",
+      "ko": "아르메니아",
+      "my": "Armenia",
+      "nl": "Armenië",
+      "pl": "Armenia",
+      "pt": "Arménia",
+      "ru": "Армения",
+      "sk": "Arménsko",
+      "th": "อาร์เมเนีย",
+      "ur": "آرمینیا",
+      "vi": "Armenia",
+      "zh": "亚美尼亚"
+    },
+    "currency": [
+      "AMD"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "297"
+    ],
+    "countryCode": "AW",
+    "countryName": "Aruba",
+    "countryNames": {
+      "cs": "Aruba",
+      "cy": "Aruba",
+      "de": "Aruba",
+      "en": "Aruba",
+      "es": "Aruba",
+      "et": "Aruba",
+      "fi": "Aruba",
+      "fr": "Aruba",
+      "hr": "Aruba",
+      "it": "Aruba",
+      "ja": "アルバ",
+      "km": "Aruba",
+      "ko": "아루바",
+      "my": "Aruba",
+      "nl": "Aruba",
+      "pl": "Aruba",
+      "pt": "Aruba",
+      "ru": "А руба",
+      "sk": "Aruba",
+      "th": "อารูบา",
+      "ur": "اروبا",
+      "vi": "Aruba",
+      "zh": "阿鲁巴"
+    },
+    "currency": [
+      "AWG"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "61"
+    ],
+    "countryCode": "AU",
+    "countryName": "Australia",
+    "countryNames": {
+      "cs": "Austrálie",
+      "cy": "Awstralia",
+      "de": "Australien",
+      "en": "Australia",
+      "es": "Australia",
+      "et": "Austraalia",
+      "fi": "Australia",
+      "fr": "Australie",
+      "hr": "Australija",
+      "it": "Australia",
+      "ja": "オーストラリア",
+      "km": "Australia",
+      "ko": "호주",
+      "my": "Australia",
+      "nl": "Australië",
+      "pl": "Australia",
+      "pt": "Austrália",
+      "ru": "Австралия",
+      "sk": "Austrália",
+      "th": "ออสเตรเลีย",
+      "ur": "آسٹریلیا",
+      "vi": "Úc",
+      "zh": "澳大利亚"
+    },
+    "currency": [
+      "AUD"
+    ],
+    "region": "Oceania",
+    "subregion": "Australia and New Zealand"
+  },
+  {
+    "callingCode": [
+      "43"
+    ],
+    "countryCode": "AT",
+    "countryName": "Austria",
+    "countryNames": {
+      "cs": "Rakousko",
+      "cy": "Awstria",
+      "de": "Österreich",
+      "en": "Austria",
+      "es": "Austria",
+      "et": "Austria",
+      "fi": "Itävalta",
+      "fr": "Autriche",
+      "hr": "Austrija",
+      "it": "Austria",
+      "ja": "オーストリア",
+      "km": "Austria",
+      "ko": " 오스트리아",
+      "my": "Austria",
+      "nl": "Oostenrijk",
+      "pl": "Austria",
+      "pt": "Áustria",
+      "ru": "Австри я",
+      "sk": "Rakúsko",
+      "th": "ออสเตรีย",
+      "ur": "آسٹریا",
+      "vi": "Austria",
+      "zh": "奥地利"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "994"
+    ],
+    "countryCode": "AZ",
+    "countryName": "Azerbaijan",
+    "countryNames": {
+      "cs": "Ázerbájdžán",
+      "cy": "Aserbaijan",
+      "de": "Aserbaidschan",
+      "en": "Azerbaijan",
+      "es": "Azerbaiyán",
+      "et": "Aserbaidžaan",
+      "fi": "Azerbaidzan",
+      "fr": "Azerbaïdjan",
+      "hr": "Azerbajdžan",
+      "it": "Azerbaijan",
+      "ja": "アゼルバイジャン",
+      "km": "Azerbaijan",
+      "ko": "아제르바이잔",
+      "my": "Azerbaijan",
+      "nl": "Azerbeidzjan",
+      "pl": "Azerbejdżan",
+      "pt": "Azerbeijão",
+      "ru": "Азербайджа н",
+      "sk": "AzerbajLJan",
+      "th": "อาเซอร์ไบจาน",
+      "ur": "آذربائیجان",
+      "vi": "Azerbaijan",
+      "zh": "阿塞拜疆"
+    },
+    "currency": [
+      "AZN"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "1242"
+    ],
+    "countryCode": "BS",
+    "countryName": "Bahamas",
+    "countryNames": {
+      "cs": "Bahamy",
+      "cy": "Bahamas",
+      "de": "Bahamas",
+      "en": "Bahamas",
+      "es": "Bahamas",
+      "et": "Bahama",
+      "fi": "Bahamasaaret",
+      "fr": "Bahamas",
+      "hr": "Bahami",
+      "it": "Bahamas",
+      "ja": " バハマ",
+      "km": "Bahamas",
+      "ko": "바하마",
+      "my": "Bahamas",
+      "nl": "Bahama’s",
+      "pl": "Bahamy",
+      "pt": "Bahamas",
+      "ru": "Багам ские Остров а",
+      "sk": "Bahamy",
+      "th": "บาฮามาส",
+      "ur": "بہاماس",
+      "vi": "Bahamas",
+      "zh": "巴哈马"
+    },
+    "currency": [
+      "BSD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "973"
+    ],
+    "countryCode": "BH",
+    "countryName": "Bahrain",
+    "countryNames": {
+      "cs": "Bahrajn",
+      "cy": "Bahrain",
+      "de": "Bahrain",
+      "en": "Bahrain",
+      "es": "Bahrein",
+      "et": "Bahrein",
+      "fi": "Bahrain",
+      "fr": "Bahreïn",
+      "hr": "Bahrein",
+      "it": "Bahrein",
+      "ja": "バーレーン",
+      "km": "Bahrain",
+      "ko": "바레인",
+      "my": "Bahrain",
+      "nl": "Bahrein",
+      "pl": "Bahrajn",
+      "pt": "Bahrein",
+      "ru": "Бахрейн",
+      "sk": "Bahrajn",
+      "th": "บาห์เรน",
+      "ur": "بحرین",
+      "vi": "Bahrain",
+      "zh": "巴林"
+    },
+    "currency": [
+      "BHD"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "880"
+    ],
+    "countryCode": "BD",
+    "countryName": "Bangladesh",
+    "countryNames": {
+      "cs": "Bangladéš",
+      "cy": "Bangladesh",
+      "de": "Bangladesch",
+      "en": "Bangladesh",
+      "es": "Bangladesh",
+      "et": "Bangladesh",
+      "fi": "Bangladesh",
+      "fr": "Bangladesh",
+      "hr": "Bangladeš",
+      "it": "Bangladesh",
+      "ja": "バングラデシュ",
+      "km": "Bangladesh",
+      "ko": "방글라데시",
+      "my": "Bangladesh",
+      "nl": "Bangladesh",
+      "pl": "Bangladesz",
+      "pt": "Bangladesh",
+      "ru": "Банглад еш",
+      "sk": "Bangladéš",
+      "th": "บังคลาเทศ",
+      "ur": "بنگلہ دیش",
+      "vi": "Bangladesh",
+      "zh": "孟加拉国"
+    },
+    "currency": [
+      "BDT"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "1246"
+    ],
+    "countryCode": "BB",
+    "countryName": "Barbados",
+    "countryNames": {
+      "cs": "Barbados",
+      "cy": "Barbados",
+      "de": "Barbados",
+      "en": "Barbados",
+      "es": "Barbados",
+      "et": "Barbados",
+      "fi": "Barbados",
+      "fr": "Barbade",
+      "hr": "Barbados",
+      "it": "Barbados",
+      "ja": " バルバドス",
+      "km": "Barbados",
+      "ko": " 바베이도스",
+      "my": "Barbados",
+      "nl": "Barbados",
+      "pl": "Barbados",
+      "pt": "Barbados",
+      "ru": "Барбадос",
+      "sk": "Barbados",
+      "th": "บาร์เบโดส",
+      "ur": "بارباڈوس",
+      "vi": "Barbados",
+      "zh": "巴巴多斯"
+    },
+    "currency": [
+      "BBD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "375"
+    ],
+    "countryCode": "BY",
+    "countryName": "Belarus",
+    "countryNames": {
+      "cs": "Bělorusko",
+      "cy": "Belarws",
+      "de": "Weißrussland",
+      "en": "Belarus",
+      "es": "Bielorrusia",
+      "et": "Valgevene",
+      "fi": "Valko-Venäjä",
+      "fr": "Biélorussie",
+      "hr": "Bjelorusija",
+      "it": "Bielorussia",
+      "ja": "ベラルーシ",
+      "km": "Belarus",
+      "ko": "벨라루스",
+      "my": "Belarus",
+      "nl": "Wit-Rusland",
+      "pl": "Białoruś",
+      "pt": "Bielorússia",
+      "ru": "Бела русь",
+      "sk": "Bielorusko",
+      "th": "เบลารุส",
+      "ur": "بیلاروس",
+      "vi": "Belarus",
+      "zh": "白俄罗斯"
+    },
+    "currency": [
+      "BYN"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "32"
+    ],
+    "countryCode": "BE",
+    "countryName": "Belgium",
+    "countryNames": {
+      "cs": "Belgie",
+      "cy": "Gwlad Belg",
+      "de": "Belgien",
+      "en": "Belgium",
+      "es": "Bélgica",
+      "et": "Belgia",
+      "fi": "Belgia",
+      "fr": "Belgique",
+      "hr": "Belgija",
+      "it": "Belgio",
+      "ja": "ベルギー",
+      "km": "Belgium",
+      "ko": "벨기에",
+      "my": "Belgium",
+      "nl": "België",
+      "pl": "Belgia",
+      "pt": "Bélgica",
+      "ru": "Бельгия",
+      "sk": "Belgicko",
+      "th": "เบลเยียม",
+      "ur": "بلجئیم",
+      "vi": "Belgium",
+      "zh": "比利时"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "501"
+    ],
+    "countryCode": "BZ",
+    "countryName": "Belize",
+    "countryNames": {
+      "cs": "Belize",
+      "cy": "Belîs",
+      "de": "Belize",
+      "en": "Belize",
+      "es": "Belice",
+      "et": "Belize",
+      "fi": "Belize",
+      "fr": "Belize",
+      "hr": "Belize",
+      "it": "Belize",
+      "ja": "ベリーズ",
+      "km": "Belize",
+      "ko": "벨리즈",
+      "my": "Belize",
+      "nl": "Belize",
+      "pl": "Belize",
+      "pt": "Belize",
+      "ru": "Белиз",
+      "sk": "Belize",
+      "th": "เบลีซ",
+      "ur": "بیلیز",
+      "vi": "Belize",
+      "zh": "伯利兹"
+    },
+    "currency": [
+      "BZD"
+    ],
+    "region": "Americas",
+    "subregion": "Central America"
+  },
+  {
+    "callingCode": [
+      "229"
+    ],
+    "countryCode": "BJ",
+    "countryName": "Benin",
+    "countryNames": {
+      "cs": "Benin",
+      "cy": "Benin",
+      "de": "Benin",
+      "en": "Benin",
+      "es": "Benín",
+      "et": "Benin",
+      "fi": "Benin",
+      "fr": "Bénin",
+      "hr": "Benin",
+      "it": "Benin",
+      "ja": "ベナン",
+      "km": "Benin",
+      "ko": "베냉",
+      "my": "Benin",
+      "nl": "Benin",
+      "pl": "Benin",
+      "pt": "Benin",
+      "ru": "Б енин",
+      "sk": "Benin",
+      "th": "เบนิน",
+      "ur": "بینن",
+      "vi": "Bénin",
+      "zh": "贝宁"
+    },
+    "currency": [
+      "XOF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "1441"
+    ],
+    "countryCode": "BM",
+    "countryName": "Bermuda",
+    "countryNames": {
+      "cs": "Bermudy",
+      "cy": "Bermiwda",
+      "de": "Bermuda",
+      "en": "Bermuda",
+      "es": "Bermudas",
+      "et": "Bermuda",
+      "fi": "Bermuda",
+      "fr": "Bermudes",
+      "hr": "Bermudi",
+      "it": "Bermuda",
+      "ja": "バ ミューダ",
+      "km": "Bermuda",
+      "ko": "버뮤다",
+      "my": "Bermuda",
+      "nl": "Bermuda",
+      "pl": "Bermudy",
+      "pt": "Bermudas",
+      "ru": "Бермудские Острова",
+      "sk": "Bermudy",
+      "th": "เบอร์มิวดา",
+      "ur": "برمودا",
+      "vi": "Bermuda",
+      "zh": "百慕 大"
+    },
+    "currency": [
+      "BMD"
+    ],
+    "region": "Americas",
+    "subregion": "North America"
+  },
+  {
+    "callingCode": [
+      "975"
+    ],
+    "countryCode": "BT",
+    "countryName": "Bhutan",
+    "countryNames": {
+      "cs": "Bhútán",
+      "cy": "Bhwtan",
+      "de": "Bhutan",
+      "en": "Bhutan",
+      "es": "Bután",
+      "et": "Bhutan",
+      "fi": "Bhutan",
+      "fr": "Bhoutan",
+      "hr": "Butan",
+      "it": "Bhutan",
+      "ja": "ブータン",
+      "km": "Bhutan",
+      "ko": "부탄",
+      "my": "Bhutan",
+      "nl": "Bhutan",
+      "pl": "Bhutan",
+      "pt": "Butão",
+      "ru": "Бутан",
+      "sk": "Bhután",
+      "th": "ภูฏาน",
+      "ur": "بھوٹان",
+      "vi": "Bhutan",
+      "zh": "不丹"
+    },
+    "currency": [
+      "BTN",
+      "INR"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "591"
+    ],
+    "countryCode": "BO",
+    "countryName": "Bolivia",
+    "countryNames": {
+      "cs": "Bolívie",
+      "cy": "Bolifia",
+      "de": "Bolivien",
+      "en": "Bolivia",
+      "es": "Bolivia",
+      "et": "Boliivia",
+      "fi": "Bolivia",
+      "fr": "Bolivie",
+      "hr": "Bolivija",
+      "it": "Bolivia",
+      "ja": "ボリビア多民族国",
+      "km": "Bolivia",
+      "ko": "볼리비아",
+      "my": "Bolivia",
+      "nl": "Bolivia",
+      "pl": "Boliwia",
+      "pt": "Bolívia",
+      "ru": "Боливия",
+      "sk": "Bolívia",
+      "th": "โบลิเวีย",
+      "ur": "بولیویا",
+      "vi": "Bolivia",
+      "zh": "玻利维亚"
+    },
+    "currency": [
+      "BOB"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "387"
+    ],
+    "countryCode": "BA",
+    "countryName": "Bosnia and Herzegovina",
+    "countryNames": {
+      "cs": "Bosna a Hercegovina",
+      "cy": "Bosnia a Hercegovina",
+      "de": "Bosnien und Herzegowina",
+      "en": "Bosnia and Herzegovina",
+      "es": "Bosnia y Herzegovina",
+      "et": "Bosnia ja Hertsegoviina",
+      "fi": "Bosnia ja Hertsegovina",
+      "fr": "Bosnie-Herzégovine",
+      "hr": "Bosna i Hercegovina",
+      "it": "Bosnia ed Erzegovina",
+      "ja": "ボスニア・ヘルツェゴビナ",
+      "km": "Bosnia and Herzegovina",
+      "ko": "보스니 아 헤르 체고비나",
+      "my": "Bosnia and Herzegovina",
+      "nl": "Bosnië en Herzegovina",
+      "pl": "Bośnia i Hercegowina",
+      "pt": "Bósnia e Herzegovina",
+      "ru": "Бос ния и Герцеговина",
+      "sk": "Bosna a Hercegovina",
+      "th": "บอสเนียและเฮอร์เซโกวีนา",
+      "ur": "بوسنیا و ہرزیگووینا",
+      "vi": "Bosna và Hercegovina",
+      "zh": "波斯尼亚和黑塞哥维那"
+    },
+    "currency": [
+      "BAM"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "267"
+    ],
+    "countryCode": "BW",
+    "countryName": "Botswana",
+    "countryNames": {
+      "cs": "Botswana",
+      "cy": "Botswana",
+      "de": "Botswana",
+      "en": "Botswana",
+      "es": "Botswana",
+      "et": "Botswana",
+      "fi": "Botswana",
+      "fr": "Botswana",
+      "hr": "Bocvana",
+      "it": "Botswana",
+      "ja": "ボツワナ",
+      "km": "Botswana",
+      "ko": "보츠와나",
+      "my": "Botswana",
+      "nl": "Botswana",
+      "pl": "Botswana",
+      "pt": "Botswana",
+      "ru": "Ботсвана",
+      "sk": "Botswana",
+      "th": "บอตสวานา",
+      "ur": "بوٹسوانا",
+      "vi": "Botswana",
+      "zh": "博茨瓦纳"
+    },
+    "currency": [
+      "BWP"
+    ],
+    "region": "Africa",
+    "subregion": "Southern Africa"
+  },
+  {
+    "callingCode": [
+      "55"
+    ],
+    "countryCode": "BR",
+    "countryName": "Brazil",
+    "countryNames": {
+      "cs": "Brazílie",
+      "cy": "Brasil",
+      "de": "Brasilien",
+      "en": "Brazil",
+      "es": "Brasil",
+      "et": "Brasiilia",
+      "fi": "Brasilia",
+      "fr": "Brésil",
+      "hr": "Brazil",
+      "it": "Brasile",
+      "ja": "ブラジル",
+      "km": "Brazil",
+      "ko": "브라질",
+      "my": "Brazil",
+      "nl": "Brazilië",
+      "pl": "Brazylia",
+      "pt": "Brasil",
+      "ru": "Бразилия",
+      "sk": "Brazília",
+      "th": "บราซิล",
+      "ur": "برازیل",
+      "vi": "Brazil",
+      "zh": "巴西"
+    },
+    "currency": [
+      "BRL"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "246"
+    ],
+    "countryCode": "IO",
+    "countryName": "British Indian Ocean Territory",
+    "countryNames": {
+      "cs": "Britské indickooceánské území",
+      "cy": "Tiriogaeth Brydeinig Cefnfor India",
+      "de": "Britisches Territorium im Indischen Ozean",
+      "en": "British Indian Ocean Territory",
+      "es": "Territorio Británico del Océano Índico",
+      "et": "Briti India ookeani ala",
+      "fi": "Brittiläinen Intian valtameren alue",
+      "fr": "Territoire britannique de l'océan Indien",
+      "hr": "Britanski Indijskooceanski teritorij",
+      "it": "Territorio britannico dell'oceano indiano",
+      "ja": "イギリス領インド洋地域",
+      "km": "British Indian Ocean Territory",
+      "ko": "인도",
+      "my": "British Indian Ocean Territory",
+      "nl": "Britse Gebieden in de Indische Oceaan",
+      "pl": "Brytyjskie Terytorium Oceanu Indyjskiego",
+      "pt": "Território Britânico do Oceano Índico",
+      "ru": "Бр итанская территория в Индийском океане",
+      "sk": "Britské indickooceánske územie",
+      "th": "British Indian Ocean Territory",
+      "ur": "برطانوی بحرہند خطہ",
+      "vi": "Lãnh thổ Ấn Độ Dương thuộc Anh",
+      "zh": "英属印度洋领地"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "1284"
+    ],
+    "countryCode": "VG",
+    "countryName": "British Virgin Islands",
+    "countryNames": {
+      "cs": "Britské Panenské ostrovy",
+      "cy": "British Virgin Islands",
+      "de": "Britische Jungferninseln",
+      "en": "British Virgin Islands",
+      "es": "Islas Vírgenes del Reino Unido",
+      "et": "Briti Neitsisaared",
+      "fi": "Neitsytsaaret",
+      "fr": "Îles Vierges britanniques",
+      "hr": "Britanski Djevičanski Otoci",
+      "it": "Isole Vergini Britanniche",
+      "ja": "イギ リス領ヴァージン諸島",
+      "km": "British Virgin Islands",
+      "ko": "영국령 버진아일랜드",
+      "my": "British Virgin Islands",
+      "nl": "Britse Maagdeneilanden",
+      "pl": "Brytyjskie Wyspy Dziewicze",
+      "pt": "Ilhas Virgens",
+      "ru": "Британские Виргинские острова",
+      "sk": "Panenské ostrovy",
+      "th": "หมู่เกาะบริติชเวอร์จิน",
+      "ur": "برطانوی جزائر ورجن",
+      "vi": "Quần đảo Virgin thuộc Anh",
+      "zh": "英属维尔京群岛"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "673"
+    ],
+    "countryCode": "BN",
+    "countryName": "Brunei",
+    "countryNames": {
+      "cs": "Brunej",
+      "cy": "Brunei",
+      "de": "Brunei",
+      "en": "Brunei",
+      "es": "Brunei",
+      "et": "Brunei",
+      "fi": "Brunei",
+      "fr": "Brunei",
+      "hr": "Brunej",
+      "it": "Brunei",
+      "ja": "ブルネイ・ダルサラーム",
+      "km": "Brunei",
+      "ko": "브루나이",
+      "my": "Brunei",
+      "nl": "Brunei",
+      "pl": "Brunei",
+      "pt": "Brunei",
+      "ru": "Бруней",
+      "sk": "Brunej",
+      "th": "บรูไน",
+      "ur": "برونائی",
+      "vi": "Brunei",
+      "zh": "文 莱"
+    },
+    "currency": [
+      "BND"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "359"
+    ],
+    "countryCode": "BG",
+    "countryName": "Bulgaria",
+    "countryNames": {
+      "cs": "Bulharsko",
+      "cy": "Bwlgaria",
+      "de": "Bulgarien",
+      "en": "Bulgaria",
+      "es": "Bulgaria",
+      "et": "Bulgaaria",
+      "fi": "Bulgaria",
+      "fr": "Bulgarie",
+      "hr": "Bugarska",
+      "it": "Bulgaria",
+      "ja": "ブルガリア",
+      "km": "Bulgaria",
+      "ko": "불가리아",
+      "my": "Bulgaria",
+      "nl": "Bulgarije",
+      "pl": "Bułgaria",
+      "pt": "Bulgária",
+      "ru": "Болгария",
+      "sk": "Bulharsko",
+      "th": "บัลแกเรีย",
+      "ur": "بلغاریہ",
+      "vi": "Bulgaria",
+      "zh": "保加利亚"
+    },
+    "currency": [
+      "BGN"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "226"
+    ],
+    "countryCode": "BF",
+    "countryName": "Burkina Faso",
+    "countryNames": {
+      "cs": "Burkina Faso",
+      "cy": "Bwrcina Ffaso",
+      "de": "Burkina Faso",
+      "en": "Burkina Faso",
+      "es": "Burkina Faso",
+      "et": "Burkina Faso",
+      "fi": "Burkina Faso",
+      "fr": "Burkina Faso",
+      "hr": "Burkina Faso",
+      "it": "Burkina Faso",
+      "ja": "ブルキナファソ",
+      "km": "Burkina Faso",
+      "ko": "부르키나파소",
+      "my": "Burkina Faso",
+      "nl": "Burkina Faso",
+      "pl": "Burkina Faso",
+      "pt": "Burkina Faso",
+      "ru": "Буркина-Фасо",
+      "sk": "Burkina Faso",
+      "th": "บูร์กินาฟาโซ",
+      "ur": "برکینا فاسو",
+      "vi": "Burkina Faso",
+      "zh": "布基纳法索"
+    },
+    "currency": [
+      "XOF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "257"
+    ],
+    "countryCode": "BI",
+    "countryName": "Burundi",
+    "countryNames": {
+      "cs": "Burundi",
+      "cy": "Bwrwndi",
+      "de": "Burundi",
+      "en": "Burundi",
+      "es": "Burundi",
+      "et": "Burundi",
+      "fi": "Burundi",
+      "fr": "Burundi",
+      "hr": "Burundi",
+      "it": "Burundi",
+      "ja": "ブルンジ",
+      "km": "Burundi",
+      "ko": "부룬디",
+      "my": "Burundi",
+      "nl": "Burundi",
+      "pl": "Burundi",
+      "pt": "Burundi",
+      "ru": "Бурунди",
+      "sk": "Burundi",
+      "th": "บุรุนดี",
+      "ur": "برونڈی",
+      "vi": "Burundi",
+      "zh": "布隆迪"
+    },
+    "currency": [
+      "BIF"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "855"
+    ],
+    "countryCode": "KH",
+    "countryName": "Cambodia",
+    "countryNames": {
+      "cs": "Kambodža",
+      "cy": "Cambodia",
+      "de": "Kambodscha",
+      "en": "Cambodia",
+      "es": "Camboya",
+      "et": "Kambodža",
+      "fi": "Kambodža",
+      "fr": "Cambodge",
+      "hr": "Kambodža",
+      "it": "Cambogia",
+      "ja": "カンボジア",
+      "km": "Cambodia",
+      "ko": "캄보디아",
+      "my": "Cambodia",
+      "nl": "Cambodja",
+      "pl": "Kambodża",
+      "pt": "Camboja",
+      "ru": "Камбоджа",
+      "sk": "Kambodža",
+      "th": "กัมพูชา",
+      "ur": "کمبوڈیا",
+      "vi": "Campuchia",
+      "zh": "柬埔寨"
+    },
+    "currency": [
+      "KHR"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "237"
+    ],
+    "countryCode": "CM",
+    "countryName": "Cameroon",
+    "countryNames": {
+      "cs": "Kamerun",
+      "cy": "Camerŵn",
+      "de": "Kamerun",
+      "en": "Cameroon",
+      "es": "Camerún",
+      "et": "Kamerun",
+      "fi": "Kamerun",
+      "fr": "Cameroun",
+      "hr": "Kamerun",
+      "it": "Camerun",
+      "ja": "カメルーン",
+      "km": "Cameroon",
+      "ko": "카메룬",
+      "my": "Cameroon",
+      "nl": "Kameroen",
+      "pl": "WybrzeŻe Kości Słoniowej",
+      "pt": "Camarões",
+      "ru": "Камерун",
+      "sk": "Kamerun",
+      "th": "แคเมอรูน",
+      "ur": "کیمرون",
+      "vi": "Cameroon",
+      "zh": "喀麦隆"
+    },
+    "currency": [
+      "XAF"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "1"
+    ],
+    "countryCode": "CA",
+    "countryName": "Canada",
+    "countryNames": {
+      "cs": "Kanada",
+      "cy": "Canada",
+      "de": "Kanada",
+      "en": "Canada",
+      "es": "Canadá",
+      "et": "Kanada",
+      "fi": "Kanada",
+      "fr": "Canada",
+      "hr": "Kanada",
+      "it": "Canada",
+      "ja": "カナダ",
+      "km": "Canada",
+      "ko": "캐나다",
+      "my": "Canada",
+      "nl": "Canada",
+      "pl": "Kanada",
+      "pt": "Canadá",
+      "ru": "Канада",
+      "sk": "Kanada",
+      "th": "แคนาดา",
+      "ur": "کینیڈا",
+      "vi": "Canada",
+      "zh": "加拿大"
+    },
+    "currency": [
+      "CAD"
+    ],
+    "region": "Americas",
+    "subregion": "North America"
+  },
+  {
+    "callingCode": [
+      "238"
+    ],
+    "countryCode": "CV",
+    "countryName": "Cape Verde",
+    "countryNames": {
+      "cs": "Kapverdy",
+      "cy": "Penrhyn Verde",
+      "de": "Kap Verde",
+      "en": "Cape Verde",
+      "es": "Cabo Verde",
+      "et": "Roheneemesaared",
+      "fi": "Kap Verde",
+      "fr": "Îles du Cap-Vert",
+      "hr": "Zelenortska Republika",
+      "it": "Capo Verde",
+      "ja": "カーボベルデ",
+      "km": "Cape Verde",
+      "ko": "카보베르데",
+      "my": "Cape Verde",
+      "nl": "Kaapverdië",
+      "pl": "Republika Zielonego Przylądka",
+      "pt": "Cabo Verde",
+      "ru": "Кабо-Верде",
+      "sk": "Kapverdy",
+      "th": "เคปเวิร์ด",
+      "ur": "کیپ ورڈی",
+      "vi": "Cape Verde",
+      "zh": "佛得角"
+    },
+    "currency": [
+      "CVE"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "599"
+    ],
+    "countryCode": "BQ",
+    "countryName": "Caribbean Netherlands",
+    "countryNames": {
+      "cs": "Karibské Nizozemsko",
+      "cy": "Caribbean Netherlands",
+      "de": "Karibische Niederlande",
+      "en": "Caribbean Netherlands",
+      "es": "Caribe Neerlandés",
+      "et": "Bonaire, Sint Eustatius ja Saba",
+      "fi": "Bonaire, Sint Eustatius ja Saba",
+      "fr": "Pays-Bas caribéens",
+      "hr": "Bonaire, Sint Eustatius i Saba",
+      "it": "Paesi Bassi caraibici",
+      "ja": "ボ ネール、シント・ユースタティウスおよびサバ",
+      "km": "Caribbean Netherlands",
+      "ko": "카리브 네덜란드",
+      "my": "Caribbean Netherlands",
+      "nl": "Caribisch Nederland",
+      "pl": "Antyle Holenderskie",
+      "pt": "Países Baixos Caribenhos",
+      "ru": "Кари бские Нидерланды",
+      "sk": "Bonaire, Sint Eustatius a Saba",
+      "th": "เนเธอร์แลนด์แคริบเบียน",
+      "ur": "کیریبین نیدرلینڈز",
+      "vi": "Caribbean Netherlands",
+      "zh": "荷蘭加勒比區"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "1345"
+    ],
+    "countryCode": "KY",
+    "countryName": "Cayman Islands",
+    "countryNames": {
+      "cs": "Kajmanské ostrovy",
+      "cy": "Ynysoedd Cayman",
+      "de": "Kaimaninseln",
+      "en": "Cayman Islands",
+      "es": "Islas Caimán",
+      "et": "Kaimanisaared",
+      "fi": "Caymansaaret",
+      "fr": "Îles Caïmans",
+      "hr": "Kajmanski otoci",
+      "it": "Isole Cayman",
+      "ja": "ケイ マン諸島",
+      "km": "Cayman Islands",
+      "ko": "케이맨 제도",
+      "my": "Cayman Islands",
+      "nl": "Caymaneilanden",
+      "pl": "Kajmany",
+      "pt": "Ilhas Caimão",
+      "ru": "Каймановы острова",
+      "sk": "Kajmanie ostrovy",
+      "th": "หมู่เกาะเคย์แมน",
+      "ur": "جزائر کیمین",
+      "vi": "Quần đảo Cayman",
+      "zh": "开曼群岛"
+    },
+    "currency": [
+      "KYD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "236"
+    ],
+    "countryCode": "CF",
+    "countryName": "Central African Republic",
+    "countryNames": {
+      "cs": "Středoafrická republika",
+      "cy": "Gweriniaeth Canolbarth Affrica",
+      "de": "Zentralafrikanische Republik",
+      "en": "Central African Republic",
+      "es": "República Centroafricana",
+      "et": "Kesk-Aafrika Vabariik",
+      "fi": "Keski-Afrikan tasavalta",
+      "fr": "République centrafricaine",
+      "hr": "Srednjoafrička Republika",
+      "it": "Repubblica Centrafricana",
+      "ja": "中央アフ リカ共和国",
+      "km": "Central African Republic",
+      "ko": "중앙아프리카 공화국",
+      "my": "Central African Republic",
+      "nl": "Centraal-Afrikaanse Republiek",
+      "pl": "Republika Środkowoafrykańska",
+      "pt": "República Centro-Africana",
+      "ru": "Центр ал ьноафриканская Респу блика",
+      "sk": "Stredoafrická republika",
+      "th": "สาธารณรัฐแอฟริกากลาง",
+      "ur": "وسطی افریقی جمہوریہ",
+      "vi": "Cộng hòa Trung Phi",
+      "zh": "中非共和国"
+    },
+    "currency": [
+      "XAF"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "235"
+    ],
+    "countryCode": "TD",
+    "countryName": "Chad",
+    "countryNames": {
+      "cs": "Čad",
+      "cy": "Tsiad",
+      "de": "Tschad",
+      "en": "Chad",
+      "es": "Chad",
+      "et": "Tšaad",
+      "fi": "Tšad",
+      "fr": "Tchad",
+      "hr": "Čad",
+      "it": "Ciad",
+      "ja": "チャド",
+      "km": "Chad",
+      "ko": "차드",
+      "my": "Chad",
+      "nl": "Tsjaad",
+      "pl": "Czad",
+      "pt": "Chade",
+      "ru": "Чад",
+      "sk": "Čad",
+      "th": "ชาด",
+      "ur": "چاڈ",
+      "vi": "Chad",
+      "zh": "乍得"
+    },
+    "currency": [
+      "XAF"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "56"
+    ],
+    "countryCode": "CL",
+    "countryName": "Chile",
+    "countryNames": {
+      "cs": "Chile",
+      "cy": "Chile",
+      "de": "Chile",
+      "en": "Chile",
+      "es": "Chile",
+      "et": "Tšiili",
+      "fi": "Chile",
+      "fr": "Chili",
+      "hr": "Čile",
+      "it": "Cile",
+      "ja": "チリ",
+      "km": "Chile",
+      "ko": "칠레",
+      "my": "Chile",
+      "nl": "Chili",
+      "pl": "Chile",
+      "pt": "Chile",
+      "ru": "Чили",
+      "sk": "Čile",
+      "th": "ชิลี",
+      "ur": "چلی",
+      "vi": "Chile",
+      "zh": "智利"
+    },
+    "currency": [
+      "CLP"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "86"
+    ],
+    "countryCode": "CN",
+    "countryName": "China",
+    "countryNames": {
+      "cs": "Čína",
+      "cy": "Tsieina",
+      "de": "China",
+      "en": "China",
+      "es": "China",
+      "et": "Hiina",
+      "fi": "Kiina",
+      "fr": "Chine",
+      "hr": "Kina",
+      "it": "Cina",
+      "ja": "中国",
+      "km": "China",
+      "ko": "중국",
+      "my": "China",
+      "nl": "China",
+      "pl": "Chiny",
+      "pt": "China",
+      "ru": "Китай",
+      "sk": "Čína",
+      "th": "จีน",
+      "ur": "چین",
+      "vi": "Trung Quốc",
+      "zh": "中国"
+    },
+    "currency": [
+      "CNY"
+    ],
+    "region": "Asia",
+    "subregion": "Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "61"
+    ],
+    "countryCode": "CX",
+    "countryName": "Christmas Island",
+    "countryNames": {
+      "cs": "Vánoční ostrov",
+      "cy": "Ynys y Nadolig",
+      "de": "Weihnachtsinsel",
+      "en": "Christmas Island",
+      "es": "Isla de Navidad",
+      "et": "Jõulusaar",
+      "fi": "Joulusaari",
+      "fr": "Île Christmas",
+      "hr": "Božićni otok",
+      "it": "Isola di Natale",
+      "ja": "クリスマス島",
+      "km": "Christmas Island",
+      "ko": "크리스마스 섬",
+      "my": "Christmas Island",
+      "nl": "Christmaseiland",
+      "pl": "Wyspa Bożego Narodzenia",
+      "pt": "Ilha do Natal",
+      "ru": "О стров Рождества",
+      "sk": "Vianočnú ostrov",
+      "th": "เกาะคริสต์มาส",
+      "ur": "جزیرہ کرسمس",
+      "vi": "Đảo Giáng sinh",
+      "zh": "圣诞岛"
+    },
+    "currency": [
+      "AUD"
+    ],
+    "region": "Oceania",
+    "subregion": "Australia and New Zealand"
+  },
+  {
+    "callingCode": [
+      "61"
+    ],
+    "countryCode": "CC",
+    "countryName": "Cocos (Keeling) Islands",
+    "countryNames": {
+      "cs": "Kokosové ostrovy",
+      "cy": "Ynysoedd Cocos",
+      "de": "Kokosinseln",
+      "en": "Cocos (Keeling) Islands",
+      "es": "Islas Cocos o Islas Keeling",
+      "et": "Kookossaared",
+      "fi": "Kookossaaret",
+      "fr": "Îles Cocos",
+      "hr": "Kokosovi Otoci",
+      "it": "Isole Cocos e Keeling",
+      "ja": "  ココス(キーリング)諸島",
+      "km": "Cocos (Keeling) Islands",
+      "ko": "코코스 제도",
+      "my": "Cocos (Keeling) Islands",
+      "nl": "Cocoseilanden",
+      "pl": "Wyspy Kokosowe",
+      "pt": "Ilhas Cocos (Keeling)",
+      "ru": "Кокосовые острова",
+      "sk": "Kokosové ostrovy",
+      "th": "Cocos",
+      "ur": "جزائر کوکوس",
+      "vi": "Cocos (Keeling) Islands",
+      "zh": "科科斯"
+    },
+    "currency": [
+      "AUD"
+    ],
+    "region": "Oceania",
+    "subregion": "Australia and New Zealand"
+  },
+  {
+    "callingCode": [
+      "57"
+    ],
+    "countryCode": "CO",
+    "countryName": "Colombia",
+    "countryNames": {
+      "cs": "Kolumbie",
+      "cy": "Colombia",
+      "de": "Kolumbien",
+      "en": "Colombia",
+      "es": "Colombia",
+      "et": "Colombia",
+      "fi": "Kolumbia",
+      "fr": "Colombie",
+      "hr": "Kolumbija",
+      "it": "Colombia",
+      "ja": "コロンビア",
+      "km": "Colombia",
+      "ko": "콜롬비아",
+      "my": "Colombia",
+      "nl": "Colombia",
+      "pl": "Kolumbia",
+      "pt": "Colômbia",
+      "ru": "Колумбия",
+      "sk": "Kolumbia",
+      "th": "โคลัมเบีย",
+      "ur": "کولمبیا",
+      "vi": "Colombia",
+      "zh": "哥伦比亚"
+    },
+    "currency": [
+      "COP"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "269"
+    ],
+    "countryCode": "KM",
+    "countryName": "Comoros",
+    "countryNames": {
+      "cs": "Komory",
+      "cy": "Y Comoros",
+      "de": "Komoren",
+      "en": "Comoros",
+      "es": "Comoras",
+      "et": "Komoorid",
+      "fi": "Komorit",
+      "fr": "Comores",
+      "hr": "Komori",
+      "it": "Comore",
+      "ja": "コモロ",
+      "km": "Comoros",
+      "ko": "코모로",
+      "my": "Comoros",
+      "nl": "Comoren",
+      "pl": "Komory",
+      "pt": "Comores",
+      "ru": "Коморы",
+      "sk": "Komory",
+      "th": "Comoros",
+      "ur": "القمری",
+      "vi": "Comoros",
+      "zh": "科摩罗"
+    },
+    "currency": [
+      "KMF"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "682"
+    ],
+    "countryCode": "CK",
+    "countryName": "Cook Islands",
+    "countryNames": {
+      "cs": "Cookovy ostrovy",
+      "cy": "Ynysoedd Cook",
+      "de": "Cookinseln",
+      "en": "Cook Islands",
+      "es": "Islas Cook",
+      "et": "Cooki saared",
+      "fi": "Cookinsaaret",
+      "fr": "Îles Cook",
+      "hr": "Cookovo Otočje",
+      "it": "Isole Cook",
+      "ja": "クック諸島",
+      "km": "Cook Islands",
+      "ko": "쿡 제도",
+      "my": "Cook Islands",
+      "nl": "Cookeilanden",
+      "pl": "Wyspy Cooka",
+      "pt": "Ilhas Cook",
+      "ru": "О строва Кука",
+      "sk": "Cookove ostrovy",
+      "th": "หมู่เกาะคุก",
+      "ur": "جزائر کک",
+      "vi": "Quần đảo Cook",
+      "zh": "库克群岛"
+    },
+    "currency": [
+      "NZD",
+      "CKD"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "506"
+    ],
+    "countryCode": "CR",
+    "countryName": "Costa Rica",
+    "countryNames": {
+      "cs": "Kostarika",
+      "cy": "Costa Rica",
+      "de": "Costa Rica",
+      "en": "Costa Rica",
+      "es": "Costa Rica",
+      "et": "Costa Rica",
+      "fi": "Costa Rica",
+      "fr": "Costa Rica",
+      "hr": "Kostarika",
+      "it": "Costa Rica",
+      "ja": "コスタリカ",
+      "km": "Costa Rica",
+      "ko": "코스타리카",
+      "my": "Costa Rica",
+      "nl": "Costa Rica",
+      "pl": "Kostaryka",
+      "pt": "Costa Rica",
+      "ru": "Коста-Рика",
+      "sk": "Kostarika",
+      "th": "คอสตาริกา",
+      "ur": "کوسٹاریکا",
+      "vi": "Costa Rica",
+      "zh": "哥斯达黎加"
+    },
+    "currency": [
+      "CRC"
+    ],
+    "region": "Americas",
+    "subregion": "Central America"
+  },
+  {
+    "callingCode": [
+      "385"
+    ],
+    "countryCode": "HR",
+    "countryName": "Croatia",
+    "countryNames": {
+      "cs": "Chorvatsko",
+      "cy": "Croatia",
+      "de": "Kroatien",
+      "en": "Croatia",
+      "es": "Croacia",
+      "et": "Horvaatia",
+      "fi": "Kroatia",
+      "fr": "Croatie",
+      "hr": "Hrvatska",
+      "it": "Croazia",
+      "ja": "クロアチア",
+      "km": "Croatia",
+      "ko": " 크로아티아",
+      "my": "Croatia",
+      "nl": "Kroatië",
+      "pl": "Chorwacja",
+      "pt": "Croácia",
+      "ru": "Хорв атия",
+      "sk": "Chorvátsko",
+      "th": "โครเอเชีย",
+      "ur": "کرویئشا",
+      "vi": "Croatia",
+      "zh": "克罗地亚"
+    },
+    "currency": [
+      "HRK"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "53"
+    ],
+    "countryCode": "CU",
+    "countryName": "Cuba",
+    "countryNames": {
+      "cs": "Kuba",
+      "cy": "Ciwba",
+      "de": "Kuba",
+      "en": "Cuba",
+      "es": "Cuba",
+      "et": "Kuuba",
+      "fi": "Kuuba",
+      "fr": "Cuba",
+      "hr": "Kuba",
+      "it": "Cuba",
+      "ja": "キューバ",
+      "km": "Cuba",
+      "ko": "쿠바",
+      "my": "Cuba",
+      "nl": "Cuba",
+      "pl": "Kuba",
+      "pt": "Cuba",
+      "ru": "Куба",
+      "sk": "Kuba",
+      "th": "คิวบา",
+      "ur": "کیوبا",
+      "vi": "Cuba",
+      "zh": "古巴"
+    },
+    "currency": [
+      "CUC",
+      "CUP"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "5999"
+    ],
+    "countryCode": "CW",
+    "countryName": "Curaçao",
+    "countryNames": {
+      "cs": "Curaçao",
+      "cy": "Curaçao",
+      "de": "Curaçao",
+      "en": "Curaçao",
+      "es": "Curazao",
+      "et": "Curaçao",
+      "fi": "Curaçao",
+      "fr": "Curaçao",
+      "hr": "Curaçao",
+      "it": "Curaçao",
+      "ja": "Curaçao",
+      "km": "Curaçao",
+      "ko": "퀴라소",
+      "my": "Curaçao",
+      "nl": "Curaçao",
+      "pl": "Curaçao",
+      "pt": "ilha da Curação",
+      "ru": "Кюрасао",
+      "sk": "Curacao",
+      "th": "คูราเซา",
+      "ur": "کیوراساؤ",
+      "vi": "Curaçao",
+      "zh": "库拉索"
+    },
+    "currency": [
+      "ANG"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "357"
+    ],
+    "countryCode": "CY",
+    "countryName": "Cyprus",
+    "countryNames": {
+      "cs": "Kypr",
+      "cy": "Cyprus",
+      "de": "Zypern",
+      "en": "Cyprus",
+      "es": "Chipre",
+      "et": "Küpros",
+      "fi": "Kypros",
+      "fr": "Chypre",
+      "hr": "Cipar",
+      "it": "Cipro",
+      "ja": "キプロス",
+      "km": "Cyprus",
+      "ko": "키프로스",
+      "my": "Cyprus",
+      "nl": "Cyprus",
+      "pl": "Cypr",
+      "pt": "Chipre",
+      "ru": "Кипр",
+      "sk": "Cyprus",
+      "th": "ไซปรัส",
+      "ur": "قبرص",
+      "vi": "Cộng hòa Síp",
+      "zh": "塞浦路斯"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "420"
+    ],
+    "countryCode": "CZ",
+    "countryName": "Czechia",
+    "countryNames": {
+      "cs": "Česko",
+      "cy": "Y Weriniaeth Tsiec",
+      "de": "Tschechien",
+      "en": "Czechia",
+      "es": "Chequia",
+      "et": "Tšehhi",
+      "fi": "Tšekki",
+      "fr": "Tchéquie",
+      "hr": "Češka",
+      "it": "Cechia",
+      "ja": "チェコ",
+      "km": "Czechia",
+      "ko": "체코",
+      "my": "Czechia",
+      "nl": "Tsjechië",
+      "pl": "Czechy",
+      "pt": "Chéquia",
+      "ru": "Чехия",
+      "sk": "Česko",
+      "th": "เช็ก",
+      "ur": "چيک",
+      "vi": "Cộng hòa Séc",
+      "zh": "捷克"
+    },
+    "currency": [
+      "CZK"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "243"
+    ],
+    "countryCode": "CD",
+    "countryName": "DR Congo",
+    "countryNames": {
+      "cs": "DR Kongo",
+      "cy": "Gweriniaeth Ddemocrataidd Congo",
+      "de": "Kongo (Dem. Rep.)",
+      "en": "DR Congo",
+      "es": "Congo (Rep. Dem.)",
+      "et": "Kongo DV",
+      "fi": "Kongon demokraattinen tasavalta",
+      "fr": "Congo (Rép. dém.)",
+      "hr": "Kongo, Demokratska Republika",
+      "it": "Congo (Rep. Dem.)",
+      "ja": "コ ンゴ民主共和国",
+      "km": "DR Congo",
+      "ko": "콩고 민주 공화국",
+      "my": "DR Congo",
+      "nl": "Congo (DRC)",
+      "pl": "Demokratyczna Republika Konga",
+      "pt": "República Democrática do Congo",
+      "ru": "Демократическая Республика  Конго",
+      "sk": "Kongo",
+      "th": "ประชาธิปไตยคองโก",
+      "ur": "کانگو",
+      "vi": "Dân chủ Congo",
+      "zh": "民主刚果"
+    },
+    "currency": [
+      "CDF"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "45"
+    ],
+    "countryCode": "DK",
+    "countryName": "Denmark",
+    "countryNames": {
+      "cs": "Dánsko",
+      "cy": "Denmarc",
+      "de": "Dänemark",
+      "en": "Denmark",
+      "es": "Dinamarca",
+      "et": "Taani",
+      "fi": "Tanska",
+      "fr": "Danemark",
+      "hr": "Danska",
+      "it": "Danimarca",
+      "ja": "デンマーク",
+      "km": "Denmark",
+      "ko": "덴마크",
+      "my": "Denmark",
+      "nl": "Denemarken",
+      "pl": "Dania",
+      "pt": "Dinamarca",
+      "ru": "Дания",
+      "sk": "Dánsko",
+      "th": "เดนมาร์ก",
+      "ur": "ڈنمارک",
+      "vi": "Đan Mạch",
+      "zh": "丹麦"
+    },
+    "currency": [
+      "DKK"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "253"
+    ],
+    "countryCode": "DJ",
+    "countryName": "Djibouti",
+    "countryNames": {
+      "cs": "Džibutsko",
+      "cy": "Jibwti",
+      "de": "Dschibuti",
+      "en": "Djibouti",
+      "es": "Djibouti",
+      "et": "Djibouti",
+      "fi": "Dijibouti",
+      "fr": "Djibouti",
+      "hr": "Džibuti",
+      "it": "Gibuti",
+      "ja": "ジブチ",
+      "km": "Djibouti",
+      "ko": "지부티",
+      "my": "Djibouti",
+      "nl": "Djibouti",
+      "pl": "Dżibuti",
+      "pt": "Djibouti",
+      "ru": "Джибути",
+      "sk": "Džibutsko",
+      "th": "จิบูตี",
+      "ur": "جبوتی",
+      "vi": "Djibouti",
+      "zh": "吉布提"
+    },
+    "currency": [
+      "DJF"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "1767"
+    ],
+    "countryCode": "DM",
+    "countryName": "Dominica",
+    "countryNames": {
+      "cs": "Dominika",
+      "cy": "Dominica",
+      "de": "Dominica",
+      "en": "Dominica",
+      "es": "Dominica",
+      "et": "Dominica",
+      "fi": "Dominica",
+      "fr": "Dominique",
+      "hr": "Dominika",
+      "it": "Dominica",
+      "ja": "ドミニカ国",
+      "km": "Dominica",
+      "ko": "도미니카 공화국",
+      "my": "Dominica",
+      "nl": "Dominica",
+      "pl": "Dominika",
+      "pt": "Dominica",
+      "ru": "Доминика",
+      "sk": "Dominika",
+      "th": "โดมินิกา",
+      "ur": "ڈومینیکا",
+      "vi": "Cộng hòa Dominica",
+      "zh": "多米尼加"
+    },
+    "currency": [
+      "XCD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "1809",
+      "1829",
+      "1849"
+    ],
+    "countryCode": "DO",
+    "countryName": "Dominican Republic",
+    "countryNames": {
+      "cs": "Dominikánská republika",
+      "cy": "Gweriniaeth Dominica",
+      "de": "Dominikanische Republik",
+      "en": "Dominican Republic",
+      "es": "República Dominicana",
+      "et": "Dominikaani Vabariik",
+      "fi": "Dominikaaninen tasavalta",
+      "fr": "République dominicaine",
+      "hr": "Dominikanska Republika",
+      "it": "Repubblica Dominicana",
+      "ja": "ドミニカ共和国",
+      "km": "Dominican Republic",
+      "ko": "도미니카 공화국",
+      "my": "Dominican Republic",
+      "nl": "Dominicaanse Republiek",
+      "pl": "Dominikana",
+      "pt": "República Dominicana",
+      "ru": "Доминика нская Республика",
+      "sk": "Dominikánska republika",
+      "th": "โดมินิกัน",
+      "ur": "ڈومینیکن",
+      "vi": "Dominican Republic",
+      "zh": "多明尼加"
+    },
+    "currency": [
+      "DOP"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "593"
+    ],
+    "countryCode": "EC",
+    "countryName": "Ecuador",
+    "countryNames": {
+      "cs": "Ekvádor",
+      "cy": "Ecwador",
+      "de": "Ecuador",
+      "en": "Ecuador",
+      "es": "Ecuador",
+      "et": "Ecuador",
+      "fi": "Ecuador",
+      "fr": "Équateur",
+      "hr": "Ekvador",
+      "it": "Ecuador",
+      "ja": "エクアドル",
+      "km": "Ecuador",
+      "ko": "에콰도르",
+      "my": "Ecuador",
+      "nl": "Ecuador",
+      "pl": "Ekwador",
+      "pt": "Equador",
+      "ru": "Э квадор",
+      "sk": "Ekvádor",
+      "th": "เอกวาดอร์",
+      "ur": "ایکواڈور",
+      "vi": "Ecuador",
+      "zh": "厄瓜多尔"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "20"
+    ],
+    "countryCode": "EG",
+    "countryName": "Egypt",
+    "countryNames": {
+      "cs": "Egypt",
+      "cy": "Yr Aifft",
+      "de": "Ägypten",
+      "en": "Egypt",
+      "es": "Egipto",
+      "et": "Egiptus",
+      "fi": "Egypti",
+      "fr": "Égypte",
+      "hr": "Egipat",
+      "it": "Egitto",
+      "ja": "エジプト",
+      "km": "Egypt",
+      "ko": "이집트",
+      "my": "Egypt",
+      "nl": "Egypte",
+      "pl": "Egipt",
+      "pt": "Egito",
+      "ru": "Еги пет",
+      "sk": "Egypt",
+      "th": "อียิปต์",
+      "ur": "مصر",
+      "vi": "Ai Cập",
+      "zh": "埃及"
+    },
+    "currency": [
+      "EGP"
+    ],
+    "region": "Africa",
+    "subregion": "Northern Africa"
+  },
+  {
+    "callingCode": [
+      "503"
+    ],
+    "countryCode": "SV",
+    "countryName": "El Salvador",
+    "countryNames": {
+      "cs": "Salvador",
+      "cy": "El Salfador",
+      "de": "El Salvador",
+      "en": "El Salvador",
+      "es": "El Salvador",
+      "et": "El Salvador",
+      "fi": "El Salvador",
+      "fr": "Salvador",
+      "hr": "Salvador",
+      "it": "El Salvador",
+      "ja": "エルサルバドル",
+      "km": "El Salvador",
+      "ko": "엘살바도르",
+      "my": "El Salvador",
+      "nl": "El Salvador",
+      "pl": "Salwador",
+      "pt": "El Salvador",
+      "ru": "Сальвадор",
+      "sk": "Salvádor",
+      "th": "เอลซัลวาดอร์",
+      "ur": "ایل سیلواڈور",
+      "vi": "El Salvador",
+      "zh": "萨 尔瓦多"
+    },
+    "currency": [
+      "SVC",
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "Central America"
+  },
+  {
+    "callingCode": [
+      "240"
+    ],
+    "countryCode": "GQ",
+    "countryName": "Equatorial Guinea",
+    "countryNames": {
+      "cs": "Rovníková Guinea",
+      "cy": "Gini Gyhydeddol",
+      "de": "Äquatorialguinea",
+      "en": "Equatorial Guinea",
+      "es": "Guinea Ecuatorial",
+      "et": "Ekvatoriaal-Guinea",
+      "fi": "Päiväntasaajan Guinea",
+      "fr": "Guinée équatoriale",
+      "hr": "Ekvatorijalna Gvineja",
+      "it": "Guinea Equatoriale",
+      "ja": "赤道ギニア",
+      "km": "Equatorial Guinea",
+      "ko": "적도 기니",
+      "my": "Equatorial Guinea",
+      "nl": "Equatoriaal-Guinea",
+      "pl": "Gwinea Równikowa",
+      "pt": "Guiné Equatorial",
+      "ru": " Эква ториальная Гвинея",
+      "sk": "Rovníková Guinea",
+      "th": "อิเควทอเรียลกินี",
+      "ur": "استوائی گنی",
+      "vi": "Guinea Xích đạo",
+      "zh": "赤道几内亚"
+    },
+    "currency": [
+      "XAF"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "291"
+    ],
+    "countryCode": "ER",
+    "countryName": "Eritrea",
+    "countryNames": {
+      "cs": "Eritrea",
+      "cy": "Eritrea",
+      "de": "Eritrea",
+      "en": "Eritrea",
+      "es": "Eritrea",
+      "et": "Eritrea",
+      "fi": "Eritrea",
+      "fr": "Érythrée",
+      "hr": "Eritreja",
+      "it": "Eritrea",
+      "ja": "エリトリ ア",
+      "km": "Eritrea",
+      "ko": "에리트레아",
+      "my": "Eritrea",
+      "nl": "Eritrea",
+      "pl": "Erytrea",
+      "pt": "Eritreia",
+      "ru": "Эритрея",
+      "sk": "Eritrea",
+      "th": "เอริเทรีย",
+      "ur": "ارتریا",
+      "vi": "Eritrea",
+      "zh": "厄立特里亚"
+    },
+    "currency": [
+      "ERN"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "372"
+    ],
+    "countryCode": "EE",
+    "countryName": "Estonia",
+    "countryNames": {
+      "cs": "Estonsko",
+      "cy": "Estonia",
+      "de": "Estland",
+      "en": "Estonia",
+      "es": "Estonia",
+      "et": "Eesti",
+      "fi": "Viro",
+      "fr": "Estonie",
+      "hr": "Estonija",
+      "it": "Estonia",
+      "ja": "エストニア",
+      "km": "Estonia",
+      "ko": "에스토니아",
+      "my": "Estonia",
+      "nl": "Estland",
+      "pl": "Estonia",
+      "pt": "Estónia",
+      "ru": " Эстония",
+      "sk": "Estónsko",
+      "th": "เอสโตเนีย",
+      "ur": "اسٹونیا",
+      "vi": "Estonia",
+      "zh": "爱沙尼亚"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "268"
+    ],
+    "countryCode": "SZ",
+    "countryName": "Eswatini",
+    "countryNames": {
+      "cs": "Svazijsko",
+      "cy": "Eswatini",
+      "de": "Swasiland",
+      "en": "Eswatini",
+      "es": "Suazilandia",
+      "et": "Svaasimaa",
+      "fi": "Swazimaa",
+      "fr": "Swaziland",
+      "hr": "Svazi",
+      "it": "Swaziland",
+      "ja": "スワジランド",
+      "km": "Eswatini",
+      "ko": "에스와티니",
+      "my": "Eswatini",
+      "nl": "Swaziland",
+      "pl": "Suazi",
+      "pt": "Suazilândia",
+      "ru": "Св азиленд",
+      "sk": "Svazijsko",
+      "th": "สวาซิแลนด์",
+      "ur": "سوازی لینڈ",
+      "vi": "Swaziland",
+      "zh": "斯威士兰"
+    },
+    "currency": [
+      "SZL"
+    ],
+    "region": "Africa",
+    "subregion": "Southern Africa"
+  },
+  {
+    "callingCode": [
+      "251"
+    ],
+    "countryCode": "ET",
+    "countryName": "Ethiopia",
+    "countryNames": {
+      "cs": "Etiopie",
+      "cy": "Ethiopia",
+      "de": "Äthiopien",
+      "en": "Ethiopia",
+      "es": "Etiopía",
+      "et": "Etioopia",
+      "fi": "Etiopia",
+      "fr": "Éthiopie",
+      "hr": "Etiopija",
+      "it": "Etiopia",
+      "ja": "エチオピア",
+      "km": "Ethiopia",
+      "ko": "에티오피아",
+      "my": "Ethiopia",
+      "nl": "Ethiopië",
+      "pl": "Etiopia",
+      "pt": "Etiópia",
+      "ru": "Э фио пия",
+      "sk": "Etiópia",
+      "th": "เอธิโอเปีย",
+      "ur": "ایتھوپیا",
+      "vi": "Ethiopia",
+      "zh": "埃塞俄比亚"
+    },
+    "currency": [
+      "ETB"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "500"
+    ],
+    "countryCode": "FK",
+    "countryName": "Falkland Islands",
+    "countryNames": {
+      "cs": "Falklandy",
+      "cy": "Falkland Islands",
+      "de": "Falklandinseln",
+      "en": "Falkland Islands",
+      "es": "Islas Malvinas",
+      "et": "Falklandi saared",
+      "fi": "Falkandinsaaret",
+      "fr": "Îles Malouines",
+      "hr": "Falklandski Otoci",
+      "it": "Isole Falkland o Isole Malvine",
+      "ja": "フォー クランド(マルビナス)諸島",
+      "km": "Falkland Islands",
+      "ko": "포클랜드 제도",
+      "my": "Falkland Islands",
+      "nl": "Falklandeilanden",
+      "pl": "Falklandy",
+      "pt": "Ilhas Malvinas",
+      "ru": " Фолклендские острова",
+      "sk": "Falklandy",
+      "th": "หมู่เกาะฟอล์คแลนด์",
+      "ur": "جزائر فاکلینڈ",
+      "vi": "Quần đảo Falkland",
+      "zh": "福克兰群岛"
+    },
+    "currency": [
+      "FKP"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "298"
+    ],
+    "countryCode": "FO",
+    "countryName": "Faroe Islands",
+    "countryNames": {
+      "cs": "Faerské ostrovy",
+      "cy": "Faroe Islands",
+      "de": "Färöer-Inseln",
+      "en": "Faroe Islands",
+      "es": "Islas Faroe",
+      "et": "Fääri saared",
+      "fi": "Färsaaret",
+      "fr": "Îles Féroé",
+      "hr": "Farski Otoci",
+      "it": "Isole Far Oer",
+      "ja": "フェロ ー諸島",
+      "km": "Faroe Islands",
+      "ko": "페 로 제도",
+      "my": "Faroe Islands",
+      "nl": "Faeröer",
+      "pl": "Wyspy Owcze",
+      "pt": "Ilhas Faroé",
+      "ru": "Фарерские острова",
+      "sk": "Faerské ostrovy",
+      "th": "หมู่เกาะแฟโร",
+      "ur": "جزائر فارو",
+      "vi": "Quần đảo Faroe",
+      "zh": "法罗群 岛"
+    },
+    "currency": [
+      "DKK"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "679"
+    ],
+    "countryCode": "FJ",
+    "countryName": "Fiji",
+    "countryNames": {
+      "cs": "Fidži",
+      "cy": "Fiji",
+      "de": "Fidschi",
+      "en": "Fiji",
+      "es": "Fiyi",
+      "et": "Fidži",
+      "fi": "Fidži",
+      "fr": "Fidji",
+      "hr": "Fiđi",
+      "it": "Figi",
+      "ja": "フィジー",
+      "km": "Fiji",
+      "ko": "피 지",
+      "my": "Fiji",
+      "nl": "Fiji",
+      "pl": "Fidżi",
+      "pt": "Fiji",
+      "ru": "Фиджи",
+      "sk": "Fidži",
+      "th": "ฟิจิ",
+      "ur": "فجی",
+      "vi": "Fiji",
+      "zh": "斐济"
+    },
+    "currency": [
+      "FJD"
+    ],
+    "region": "Oceania",
+    "subregion": "Melanesia"
+  },
+  {
+    "callingCode": [
+      "358"
+    ],
+    "countryCode": "FI",
+    "countryName": "Finland",
+    "countryNames": {
+      "cs": "Finsko",
+      "cy": "Finland",
+      "de": "Finnland",
+      "en": "Finland",
+      "es": "Finlandia",
+      "et": "Soome",
+      "fi": "Suomi",
+      "fr": "Finlande",
+      "hr": "Finska",
+      "it": "Finlandia",
+      "ja": "フィンランド",
+      "km": "Finland",
+      "ko": "핀란드",
+      "my": "Finland",
+      "nl": "Finland",
+      "pl": "Finlandia",
+      "pt": "Finlândia",
+      "ru": "Финляндия",
+      "sk": "Fínsko",
+      "th": "ฟินแลนด์",
+      "ur": "فن لینڈ",
+      "vi": "Phần Lan",
+      "zh": "芬兰"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "33"
+    ],
+    "countryCode": "FR",
+    "countryName": "France",
+    "countryNames": {
+      "cs": "Francie",
+      "cy": "France",
+      "de": "Frankreich",
+      "en": "France",
+      "es": "Francia",
+      "et": "Prantsusmaa",
+      "fi": "Ranska",
+      "fr": "France",
+      "hr": "Francuska",
+      "it": "Francia",
+      "ja": "フラ ンス",
+      "km": "France",
+      "ko": "프랑스",
+      "my": "France",
+      "nl": "Frankrijk",
+      "pl": "Francja",
+      "pt": "França",
+      "ru": "Франция",
+      "sk": "Francúzsko",
+      "th": "ฝรั่งเศส",
+      "ur": "فرانس",
+      "vi": "Pháp",
+      "zh": "法国"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "594"
+    ],
+    "countryCode": "GF",
+    "countryName": "French Guiana",
+    "countryNames": {
+      "cs": "Francouzská Guyana",
+      "cy": "French Guiana",
+      "de": "Französisch-Guayana",
+      "en": "French Guiana",
+      "es": "Guayana Francesa",
+      "et": "Prantsuse Guajaana",
+      "fi": "Ranskan Guayana",
+      "fr": "Guyane",
+      "hr": "Francuska Gvajana",
+      "it": "Guyana francese",
+      "ja": "フランス領ギアナ",
+      "km": "French Guiana",
+      "ko": "프랑스령  기아나",
+      "my": "French Guiana",
+      "nl": "Frans-Guyana",
+      "pl": "Gujana Francuska",
+      "pt": "Guiana Francesa",
+      "ru": "Французская Гвиана",
+      "sk": "Guyana",
+      "th": "เฟรนช์เกียนา",
+      "ur": "فرانسیسی گیانا",
+      "vi": "Guiana thuộc Pháp",
+      "zh": "法属圭亚那"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "689"
+    ],
+    "countryCode": "PF",
+    "countryName": "French Polynesia",
+    "countryNames": {
+      "cs": "Francouzská Polynésie",
+      "cy": "French Polynesia",
+      "de": "Französisch-Polynesien",
+      "en": "French Polynesia",
+      "es": "Polinesia Francesa",
+      "et": "Prantsuse Polüneesia",
+      "fi": "Ranskan Polynesia",
+      "fr": "Polynésie française",
+      "hr": "Francuska Polinezija",
+      "it": "Polinesia Francese",
+      "ja": "フランス領ポリネシア",
+      "km": "French Polynesia",
+      "ko": "프랑스령 폴리네시아",
+      "my": "French Polynesia",
+      "nl": "Frans-Polynesië",
+      "pl": "Polinezja Francuska",
+      "pt": "Polinésia Francesa",
+      "ru": "Француз ская Пол инезия",
+      "sk": "Francúzska Polynézia",
+      "th": "เฟรนช์โพลินีเซีย",
+      "ur": "فرانسیسی پولینیشیا",
+      "vi": "Polynesia thuộc Pháp",
+      "zh": "法属波利尼西亚"
+    },
+    "currency": [
+      "XPF"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "241"
+    ],
+    "countryCode": "GA",
+    "countryName": "Gabon",
+    "countryNames": {
+      "cs": "Gabon",
+      "cy": "Gabon",
+      "de": "Gabun",
+      "en": "Gabon",
+      "es": "Gabón",
+      "et": "Gabon",
+      "fi": "Gabon",
+      "fr": "Gabon",
+      "hr": "Gabon",
+      "it": "Gabon",
+      "ja": "ガボン",
+      "km": "Gabon",
+      "ko": "가봉",
+      "my": "Gabon",
+      "nl": "Gabon",
+      "pl": "Gabon",
+      "pt": "Gabão",
+      "ru": "Габон",
+      "sk": "Gabon",
+      "th": "กาบอง",
+      "ur": "گیبون",
+      "vi": "Gabon",
+      "zh": "加蓬"
+    },
+    "currency": [
+      "XAF"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "220"
+    ],
+    "countryCode": "GM",
+    "countryName": "Gambia",
+    "countryNames": {
+      "cs": "Gambie",
+      "cy": "Gambia",
+      "de": "Gambia",
+      "en": "Gambia",
+      "es": "Gambia",
+      "et": "Gambia",
+      "fi": "Gambia",
+      "fr": "Gambie",
+      "hr": "Gambija",
+      "it": "Gambia",
+      "ja": "ガンビア",
+      "km": "Gambia",
+      "ko": "감비아",
+      "my": "Gambia",
+      "nl": "Gambia",
+      "pl": "Gambia",
+      "pt": "Gâmbia",
+      "ru": "Гамбия",
+      "sk": "Gambia",
+      "th": "แกมเบีย",
+      "ur": "گیمبیا",
+      "vi": "Gambia",
+      "zh": "冈比亚"
+    },
+    "currency": [
+      "GMD"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "995"
+    ],
+    "countryCode": "GE",
+    "countryName": "Georgia",
+    "countryNames": {
+      "cs": "Gruzie",
+      "cy": "Georgia",
+      "de": "Georgien",
+      "en": "Georgia",
+      "es": "Georgia",
+      "et": "Gruusia",
+      "fi": "Georgia",
+      "fr": "Géorgie",
+      "hr": "Gruzija",
+      "it": "Georgia",
+      "ja": "グルジア",
+      "km": "Georgia",
+      "ko": "조지아",
+      "my": "Georgia",
+      "nl": "Georgië",
+      "pl": "Gruzja",
+      "pt": "Geórgia",
+      "ru": "Грузия",
+      "sk": "Gruzínsko",
+      "th": "จอร์เจีย",
+      "ur": "جارجیا",
+      "vi": "Georgia",
+      "zh": "格鲁吉 亚"
+    },
+    "currency": [
+      "GEL"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "49"
+    ],
+    "countryCode": "DE",
+    "countryName": "Germany",
+    "countryNames": {
+      "cs": "Německo",
+      "cy": "Germany",
+      "de": "Deutschland",
+      "en": "Germany",
+      "es": "Alemania",
+      "et": "Saksamaa",
+      "fi": "Saksa",
+      "fr": "Allemagne",
+      "hr": "Njemačka",
+      "it": "Germania",
+      "ja": "ドイツ",
+      "km": "Germany",
+      "ko": "독일",
+      "my": "Germany",
+      "nl": "Duitsland",
+      "pl": "Niemcy",
+      "pt": "Alemanha",
+      "ru": "Германи я",
+      "sk": "Nemecko",
+      "th": "เยอรมนี",
+      "ur": "جرمنی",
+      "vi": "Germany",
+      "zh": "德国"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "233"
+    ],
+    "countryCode": "GH",
+    "countryName": "Ghana",
+    "countryNames": {
+      "cs": "Ghana",
+      "cy": "Ghana",
+      "de": "Ghana",
+      "en": "Ghana",
+      "es": "Ghana",
+      "et": "Ghana",
+      "fi": "Ghana",
+      "fr": "Ghana",
+      "hr": "Gana",
+      "it": "Ghana",
+      "ja": "ガーナ",
+      "km": "Ghana",
+      "ko": "가나",
+      "my": "Ghana",
+      "nl": "Ghana",
+      "pl": "Ghana",
+      "pt": "Gana",
+      "ru": "Г ана",
+      "sk": "Ghana",
+      "th": "กานา",
+      "ur": "گھانا",
+      "vi": "Ghana",
+      "zh": "加纳"
+    },
+    "currency": [
+      "GHS"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "350"
+    ],
+    "countryCode": "GI",
+    "countryName": "Gibraltar",
+    "countryNames": {
+      "cs": "Gibraltar",
+      "cy": "Gibraltar",
+      "de": "Gibraltar",
+      "en": "Gibraltar",
+      "es": "Gibraltar",
+      "et": "Gibraltar",
+      "fi": "Gibraltar",
+      "fr": "Gibraltar",
+      "hr": "Gibraltar",
+      "it": "Gibilterra",
+      "ja": "ジブラルタル",
+      "km": "Gibraltar",
+      "ko": "지브롤터",
+      "my": "Gibraltar",
+      "nl": "Gibraltar",
+      "pl": "Gibraltar",
+      "pt": "Gibraltar",
+      "ru": "Гибралтар",
+      "sk": "Gibraltár",
+      "th": "ยิบรอลตา",
+      "ur": "جبل الطارق",
+      "vi": "Gibraltar",
+      "zh": "直布罗陀"
+    },
+    "currency": [
+      "GIP"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "30"
+    ],
+    "countryCode": "GR",
+    "countryName": "Greece",
+    "countryNames": {
+      "cs": "Řecko",
+      "cy": "Greece",
+      "de": "Griechenland",
+      "en": "Greece",
+      "es": "Grecia",
+      "et": "Kreeka",
+      "fi": "Kreikka",
+      "fr": "Grèce",
+      "hr": "Grčka",
+      "it": "Grecia",
+      "ja": "ギリシャ",
+      "km": "Greece",
+      "ko": "그리스",
+      "my": "Greece",
+      "nl": "Griekenland",
+      "pl": "Grecja",
+      "pt": "Grécia",
+      "ru": "Грец ия",
+      "sk": "Greécko",
+      "th": "กรีซ",
+      "ur": "یونان",
+      "vi": "Hy Lạp",
+      "zh": "希腊"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "299"
+    ],
+    "countryCode": "GL",
+    "countryName": "Greenland",
+    "countryNames": {
+      "cs": "Grónsko",
+      "cy": "Greenland",
+      "de": "Grönland",
+      "en": "Greenland",
+      "es": "Groenlandia",
+      "et": "Gröönimaa",
+      "fi": "Groönlanti",
+      "fr": "Groenland",
+      "hr": "Grenland",
+      "it": "Groenlandia",
+      "ja": "グリーンランド",
+      "km": "Greenland",
+      "ko": "그린란드",
+      "my": "Greenland",
+      "nl": "Groenland",
+      "pl": "Grenlandia",
+      "pt": "Gronelândia",
+      "ru": "Гренлан дия",
+      "sk": "Grónsko",
+      "th": "กรีนแลนด์",
+      "ur": "گرین لینڈ",
+      "vi": "Greenland",
+      "zh": "格陵兰"
+    },
+    "currency": [
+      "DKK"
+    ],
+    "region": "Americas",
+    "subregion": "North America"
+  },
+  {
+    "callingCode": [
+      "1473"
+    ],
+    "countryCode": "GD",
+    "countryName": "Grenada",
+    "countryNames": {
+      "cs": "Grenada",
+      "cy": "Grenada",
+      "de": "Grenada",
+      "en": "Grenada",
+      "es": "Grenada",
+      "et": "Grenada",
+      "fi": "Grenada",
+      "fr": "Grenade",
+      "hr": "Grenada",
+      "it": "Grenada",
+      "ja": "グレナダ",
+      "km": "Grenada",
+      "ko": "그레나다",
+      "my": "Grenada",
+      "nl": "Grenada",
+      "pl": "Grenada",
+      "pt": "Granada",
+      "ru": "Гренада",
+      "sk": "Grenada",
+      "th": "เกรเนดา",
+      "ur": "گریناڈا",
+      "vi": "Grenada",
+      "zh": "格林纳达"
+    },
+    "currency": [
+      "XCD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "590"
+    ],
+    "countryCode": "GP",
+    "countryName": "Guadeloupe",
+    "countryNames": {
+      "cs": "Guadeloupe",
+      "cy": "Guadeloupe",
+      "de": "Guadeloupe",
+      "en": "Guadeloupe",
+      "es": "Guadalupe",
+      "et": "Guadeloupe",
+      "fi": "Guadeloupe",
+      "fr": "Guadeloupe",
+      "hr": "Gvadalupa",
+      "it": "Guadeloupa",
+      "ja": "グアドループ",
+      "km": "Guadeloupe",
+      "ko": "과들루프",
+      "my": "Guadeloupe",
+      "nl": "Guadeloupe",
+      "pl": "Gwadelupa",
+      "pt": "Guadalupe",
+      "ru": "Гваделупа",
+      "sk": "Guadeloupe",
+      "th": "กวาเดอลูป",
+      "ur": "گواڈیلوپ",
+      "vi": "Guadeloupe",
+      "zh": "瓜德罗普岛"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "1671"
+    ],
+    "countryCode": "GU",
+    "countryName": "Guam",
+    "countryNames": {
+      "cs": "Guam",
+      "cy": "Guam",
+      "de": "Guam",
+      "en": "Guam",
+      "es": "Guam",
+      "et": "Guam",
+      "fi": "Guam",
+      "fr": "Guam",
+      "hr": "Guam",
+      "it": "Guam",
+      "ja": "グアム",
+      "km": "Guam",
+      "ko": "괌",
+      "my": "Guam",
+      "nl": "Guam",
+      "pl": "Guam",
+      "pt": "Guam",
+      "ru": "Гуам",
+      "sk": "Guam",
+      "th": "กวม",
+      "ur": "گوام",
+      "vi": "Guam",
+      "zh": "关岛"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Oceania",
+    "subregion": "Micronesia"
+  },
+  {
+    "callingCode": [
+      "502"
+    ],
+    "countryCode": "GT",
+    "countryName": "Guatemala",
+    "countryNames": {
+      "cs": "Guatemala",
+      "cy": "Guatemala",
+      "de": "Guatemala",
+      "en": "Guatemala",
+      "es": "Guatemala",
+      "et": "Guatemala",
+      "fi": "Guatemala",
+      "fr": "Guatemala",
+      "hr": "Gvatemala",
+      "it": "Guatemala",
+      "ja": "グアテマラ",
+      "km": "Guatemala",
+      "ko": "과테말라",
+      "my": "Guatemala",
+      "nl": "Guatemala",
+      "pl": "Gwatemala",
+      "pt": "Guatemala",
+      "ru": "Гватемала",
+      "sk": "Guatemala",
+      "th": "กัวเตมาลา",
+      "ur": "گواتیمالا",
+      "vi": "Guatemala",
+      "zh": "危地马拉"
+    },
+    "currency": [
+      "GTQ"
+    ],
+    "region": "Americas",
+    "subregion": "Central America"
+  },
+  {
+    "callingCode": [
+      "44"
+    ],
+    "countryCode": "GG",
+    "countryName": "Guernsey",
+    "countryNames": {
+      "cs": "Guernsey",
+      "cy": "Guernsey",
+      "de": "Guernsey",
+      "en": "Guernsey",
+      "es": "Guernsey",
+      "et": "Guernsey",
+      "fi": "Guernsey",
+      "fr": "Guernesey",
+      "hr": "Guernsey",
+      "it": "Guernsey",
+      "ja": "ガーンジー",
+      "km": "Guernsey",
+      "ko": "건지 섬",
+      "my": "Guernsey",
+      "nl": "Guernsey",
+      "pl": "Guernsey",
+      "pt": "Guernsey",
+      "ru": "Гернси",
+      "sk": "Guernsey",
+      "th": "เกิร์นซีย์",
+      "ur": "گرنزی",
+      "vi": "Đảo Guernsey",
+      "zh": " 根西岛"
+    },
+    "currency": [
+      "GBP"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "224"
+    ],
+    "countryCode": "GN",
+    "countryName": "Guinea",
+    "countryNames": {
+      "cs": "Guinea",
+      "cy": "Guinea",
+      "de": "Guinea",
+      "en": "Guinea",
+      "es": "Guinea",
+      "et": "Guinea",
+      "fi": "Guinea",
+      "fr": "Guinée",
+      "hr": "Gvineja",
+      "it": "Guinea",
+      "ja": "ギニア",
+      "km": "Guinea",
+      "ko": "기니",
+      "my": "Guinea",
+      "nl": "Guinee",
+      "pl": "Gwinea",
+      "pt": "Guiné",
+      "ru": "Гвинея",
+      "sk": "Guinea",
+      "th": "กินี",
+      "ur": "گنی",
+      "vi": "Guinea",
+      "zh": "几内亚"
+    },
+    "currency": [
+      "GNF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "245"
+    ],
+    "countryCode": "GW",
+    "countryName": "Guinea-Bissau",
+    "countryNames": {
+      "cs": "Guinea-Bissau",
+      "cy": "Guinea-Bissau",
+      "de": "Guinea-Bissau",
+      "en": "Guinea-Bissau",
+      "es": "Guinea-Bisáu",
+      "et": "Guinea-Bissau",
+      "fi": "Guinea-Bissau",
+      "fr": "Guinée-Bissau",
+      "hr": "Gvineja Bisau",
+      "it": "Guinea-Bissau",
+      "ja": "ギニアビサウ",
+      "km": "Guinea-Bissau",
+      "ko": "기니비사우",
+      "my": "Guinea-Bissau",
+      "nl": "Guinee-Bissau",
+      "pl": "Gwinea Bissau",
+      "pt": "Guiné-Bissau",
+      "ru": " Гвинея-Бисау",
+      "sk": "Guinea-Bissau",
+      "th": "กินีบิสเซา",
+      "ur": "گنی بساؤ",
+      "vi": "Guinea-Bissau",
+      "zh": "几内亚比绍"
+    },
+    "currency": [
+      "XOF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "592"
+    ],
+    "countryCode": "GY",
+    "countryName": "Guyana",
+    "countryNames": {
+      "cs": "Guyana",
+      "cy": "Guyana",
+      "de": "Guyana",
+      "en": "Guyana",
+      "es": "Guyana",
+      "et": "Guyana",
+      "fi": "Guayana",
+      "fr": "Guyana",
+      "hr": "Gvajana",
+      "it": "Guyana",
+      "ja": "ガイアナ",
+      "km": "Guyana",
+      "ko": "가이아 나",
+      "my": "Guyana",
+      "nl": "Guyana",
+      "pl": "Gujana",
+      "pt": "Guiana",
+      "ru": "Гайана",
+      "sk": "Guyana",
+      "th": "กายอานา",
+      "ur": "گیانا",
+      "vi": "Guyana",
+      "zh": "圭亚那"
+    },
+    "currency": [
+      "GYD"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "509"
+    ],
+    "countryCode": "HT",
+    "countryName": "Haiti",
+    "countryNames": {
+      "cs": "Haiti",
+      "cy": "Haiti",
+      "de": "Haiti",
+      "en": "Haiti",
+      "es": "Haiti",
+      "et": "Haiti",
+      "fi": "Haiti",
+      "fr": "Haïti",
+      "hr": "Haiti",
+      "it": "Haiti",
+      "ja": "ハイチ",
+      "km": "Haiti",
+      "ko": "아이티",
+      "my": "Haiti",
+      "nl": "Haïti",
+      "pl": "Haiti",
+      "pt": "Haiti",
+      "ru": "Гаи ти",
+      "sk": "Haiti",
+      "th": "เฮติ",
+      "ur": "ہیٹی",
+      "vi": "Haiti",
+      "zh": "海地"
+    },
+    "currency": [
+      "HTG",
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "504"
+    ],
+    "countryCode": "HN",
+    "countryName": "Honduras",
+    "countryNames": {
+      "cs": "Honduras",
+      "cy": "Honduras",
+      "de": "Honduras",
+      "en": "Honduras",
+      "es": "Honduras",
+      "et": "Honduras",
+      "fi": "Honduras",
+      "fr": "Honduras",
+      "hr": "Honduras",
+      "it": "Honduras",
+      "ja": "ホンジュラス",
+      "km": "Honduras",
+      "ko": "온두라 스",
+      "my": "Honduras",
+      "nl": "Honduras",
+      "pl": "Honduras",
+      "pt": "Honduras",
+      "ru": " Гондурас",
+      "sk": "Honduras",
+      "th": "ฮอนดูรัส",
+      "ur": "ہونڈوراس",
+      "vi": "Honduras",
+      "zh": "洪都拉斯"
+    },
+    "currency": [
+      "HNL"
+    ],
+    "region": "Americas",
+    "subregion": "Central America"
+  },
+  {
+    "callingCode": [
+      "852"
+    ],
+    "countryCode": "HK",
+    "countryName": "Hong Kong",
+    "countryNames": {
+      "cs": "Hongkong",
+      "cy": "Hong Kong",
+      "de": "Hongkong",
+      "en": "Hong Kong",
+      "es": "Hong Kong",
+      "et": "Hongkong",
+      "fi": "Hongkong",
+      "fr": "Hong Kong",
+      "hr": "Hong Kong",
+      "it": "Hong Kong",
+      "ja": "香港",
+      "km": "Hong Kong",
+      "ko": "홍콩",
+      "my": "Hong Kong",
+      "nl": "Hongkong",
+      "pl": "Hongkong",
+      "pt": "Hong Kong",
+      "ru": "Гонконг",
+      "sk": "Hongkong",
+      "th": "ฮ่องกง",
+      "ur": "ہانگ کانگ",
+      "vi": "Hồng Kông",
+      "zh": "香港"
+    },
+    "currency": [
+      "HKD"
+    ],
+    "region": "Asia",
+    "subregion": "Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "36"
+    ],
+    "countryCode": "HU",
+    "countryName": "Hungary",
+    "countryNames": {
+      "cs": "Maďarsko",
+      "cy": "Hungary",
+      "de": "Ungarn",
+      "en": "Hungary",
+      "es": "Hungría",
+      "et": "Ungari",
+      "fi": "Unkari",
+      "fr": "Hongrie",
+      "hr": "Mađarska",
+      "it": "Ungheria",
+      "ja": "ハンガリー",
+      "km": "Hungary",
+      "ko": "헝가리",
+      "my": "Hungary",
+      "nl": "Hongarije",
+      "pl": "Węgry",
+      "pt": "Hungria",
+      "ru": " Венгрия",
+      "sk": "Maďarsko",
+      "th": "ฮังการี",
+      "ur": "مجارستان",
+      "vi": "Hungary",
+      "zh": "匈牙利"
+    },
+    "currency": [
+      "HUF"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "354"
+    ],
+    "countryCode": "IS",
+    "countryName": "Iceland",
+    "countryNames": {
+      "cs": "Island",
+      "cy": "Iceland",
+      "de": "Island",
+      "en": "Iceland",
+      "es": "Islandia",
+      "et": "Island",
+      "fi": "Islanti",
+      "fr": "Islande",
+      "hr": "Island",
+      "it": "Islanda",
+      "ja": "アイス ランド",
+      "km": "Iceland",
+      "ko": "아이슬란드",
+      "my": "Iceland",
+      "nl": "IJsland",
+      "pl": "Islandia",
+      "pt": "Islândia",
+      "ru": "Исландия",
+      "sk": "Island",
+      "th": "ไอซ์แลนด์",
+      "ur": "آئس لینڈ",
+      "vi": "Iceland",
+      "zh": "冰岛"
+    },
+    "currency": [
+      "ISK"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "91"
+    ],
+    "countryCode": "IN",
+    "countryName": "India",
+    "countryNames": {
+      "cs": "Indie",
+      "cy": "India",
+      "de": "Indien",
+      "en": "India",
+      "es": "India",
+      "et": "India",
+      "fi": "Intia",
+      "fr": "Inde",
+      "hr": "Indija",
+      "it": "India",
+      "ja": "インド",
+      "km": "India",
+      "ko": " 인도",
+      "my": "India",
+      "nl": "India",
+      "pl": "Indie",
+      "pt": "Índia",
+      "ru": "Индия",
+      "sk": "India",
+      "th": "อินเดีย",
+      "ur": "بھارت",
+      "vi": "India",
+      "zh": "印度"
+    },
+    "currency": [
+      "INR"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "62"
+    ],
+    "countryCode": "ID",
+    "countryName": "Indonesia",
+    "countryNames": {
+      "cs": "Indonésie",
+      "cy": "Indonesia",
+      "de": "Indonesien",
+      "en": "Indonesia",
+      "es": "Indonesia",
+      "et": "Indoneesia",
+      "fi": "Indonesia",
+      "fr": "Indonésie",
+      "hr": "Indonezija",
+      "it": "Indonesia",
+      "ja": " インドネシ ア",
+      "km": "Indonesia",
+      "ko": "인도네시아",
+      "my": "Indonesia",
+      "nl": "Indonesië",
+      "pl": "Indonezja",
+      "pt": "Indonésia",
+      "ru": "Индонезия",
+      "sk": "Indonézia",
+      "th": "อินโดนีเซีย",
+      "ur": "انڈونیشیا",
+      "vi": "Indonesia",
+      "zh": "印度尼 西亚"
+    },
+    "currency": [
+      "IDR"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "98"
+    ],
+    "countryCode": "IR",
+    "countryName": "Iran",
+    "countryNames": {
+      "cs": "Írán",
+      "cy": "Iran",
+      "de": "Iran",
+      "en": "Iran",
+      "es": "Iran",
+      "et": "Iraan",
+      "fi": "Iran",
+      "fr": "Iran",
+      "hr": "Iran",
+      "it": "Iran",
+      "ja": "イラン・イスラム共和国",
+      "km": "Iran",
+      "ko": "이란",
+      "my": "Iran",
+      "nl": "Iran",
+      "pl": "Iran",
+      "pt": "Irão",
+      "ru": "Иран",
+      "sk": "Irán",
+      "th": "อิหร่าน",
+      "ur": "ایران",
+      "vi": "Iran",
+      "zh": "伊朗"
+    },
+    "currency": [
+      "IRR"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "964"
+    ],
+    "countryCode": "IQ",
+    "countryName": "Iraq",
+    "countryNames": {
+      "cs": "Irák",
+      "cy": "Iraq",
+      "de": "Irak",
+      "en": "Iraq",
+      "es": "Irak",
+      "et": "Iraak",
+      "fi": "Irak",
+      "fr": "Irak",
+      "hr": "Irak",
+      "it": "Iraq",
+      "ja": "イラク",
+      "km": "Iraq",
+      "ko": "이라크",
+      "my": "Iraq",
+      "nl": "Irak",
+      "pl": "Irak",
+      "pt": "Iraque",
+      "ru": "Ирак",
+      "sk": "Irak",
+      "th": "อิรัก",
+      "ur": "عراق",
+      "vi": "Iraq",
+      "zh": "伊拉克"
+    },
+    "currency": [
+      "IQD"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "353"
+    ],
+    "countryCode": "IE",
+    "countryName": "Ireland",
+    "countryNames": {
+      "cs": "Irsko",
+      "cy": "Ireland",
+      "de": "Irland",
+      "en": "Ireland",
+      "es": "Irlanda",
+      "et": "Iirimaa",
+      "fi": "Irlanti",
+      "fr": "Irlande",
+      "hr": "Irska",
+      "it": "Irlanda",
+      "ja": "アイルランド",
+      "km": "Ireland",
+      "ko": " 아일랜드",
+      "my": "Ireland",
+      "nl": "Ierland",
+      "pl": "Irlandia",
+      "pt": "Irlanda",
+      "ru": "Ирландия",
+      "sk": "Írsko",
+      "th": "ไอร์แลนด์",
+      "ur": "جزیرہ آئرلینڈ",
+      "vi": "Ireland",
+      "zh": "爱尔兰"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "44"
+    ],
+    "countryCode": "IM",
+    "countryName": "Isle of Man",
+    "countryNames": {
+      "cs": "Ostrov Man",
+      "cy": "Isle of Man",
+      "de": "Insel Man",
+      "en": "Isle of Man",
+      "es": "Isla de Man",
+      "et": "Mani saar",
+      "fi": "Mansaari",
+      "fr": "Île de Man",
+      "hr": "Otok Man",
+      "it": "Isola di Man",
+      "ja": "マン島",
+      "km": "Isle of Man",
+      "ko": "맨섬",
+      "my": "Isle of Man",
+      "nl": "Isle of Man",
+      "pl": "Wyspa Man",
+      "pt": "Ilha de Man",
+      "ru": "Остров Мэн",
+      "sk": "Man",
+      "th": "Isle of Man",
+      "ur": "آئل آف مین",
+      "vi": "Isle of Man",
+      "zh": "马恩岛"
+    },
+    "currency": [
+      "GBP"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "972"
+    ],
+    "countryCode": "IL",
+    "countryName": "Israel",
+    "countryNames": {
+      "cs": "Izrael",
+      "cy": "Israel",
+      "de": "Israel",
+      "en": "Israel",
+      "es": "Israel",
+      "et": "Iisrael",
+      "fi": "Israel",
+      "fr": "Israël",
+      "hr": "Izrael",
+      "it": "Israele",
+      "ja": "イスラエル",
+      "km": "Israel",
+      "ko": " 이스라엘",
+      "my": "Israel",
+      "nl": "Israël",
+      "pl": "Izrael",
+      "pt": "Israel",
+      "ru": "Израиль",
+      "sk": "Izrael",
+      "th": "อิสราเอล",
+      "ur": "اسرائیل",
+      "vi": "Israel",
+      "zh": "以色列"
+    },
+    "currency": [
+      "ILS"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "39"
+    ],
+    "countryCode": "IT",
+    "countryName": "Italy",
+    "countryNames": {
+      "cs": "Itálie",
+      "cy": "Italy",
+      "de": "Italien",
+      "en": "Italy",
+      "es": "Italia",
+      "et": "Itaalia",
+      "fi": "Italia",
+      "fr": "Italie",
+      "hr": "Italija",
+      "it": "Italia",
+      "ja": "イタリア",
+      "km": "Italy",
+      "ko": "이탈리아",
+      "my": "Italy",
+      "nl": "Italië",
+      "pl": "Włochy",
+      "pt": "Itália",
+      "ru": "Италия",
+      "sk": "Taliansko",
+      "th": "อิตาลี",
+      "ur": "اطالیہ",
+      "vi": "Italy",
+      "zh": "意大利"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "225"
+    ],
+    "countryCode": "CI",
+    "countryName": "Ivory Coast",
+    "countryNames": {
+      "cs": "Pobřeží slonoviny",
+      "cy": "Ivory Coast",
+      "de": "Elfenbeinküste",
+      "en": "Ivory Coast",
+      "es": "Costa de Marfil",
+      "et": "Elevandiluurannik",
+      "fi": "Norsunluurannikko",
+      "fr": "Côte d'Ivoire",
+      "hr": "Obala Bjelokosti",
+      "it": "Costa d'Avorio",
+      "ja": "コートジボワール",
+      "km": "Ivory Coast",
+      "ko": "코트디부아르",
+      "my": "Ivory Coast",
+      "nl": "Ivoorkust",
+      "pl": "WybrzeŻe Kości Słoniowej",
+      "pt": "Costa do Marfim",
+      "ru": "Кот-д’Ивуар",
+      "sk": "Pobržie Slonoviny",
+      "th": "ไอวอรีโคสต์",
+      "ur": "آئیوری کوسٹ",
+      "vi": "Bờ Biển Ngà",
+      "zh": "科特迪瓦"
+    },
+    "currency": [
+      "XOF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "1876"
+    ],
+    "countryCode": "JM",
+    "countryName": "Jamaica",
+    "countryNames": {
+      "cs": "Jamajka",
+      "cy": "Jamaica",
+      "de": "Jamaika",
+      "en": "Jamaica",
+      "es": "Jamaica",
+      "et": "Jamaica",
+      "fi": "Jamaika",
+      "fr": "Jamaïque",
+      "hr": "Jamajka",
+      "it": "Giamaica",
+      "ja": "ジャマイカ",
+      "km": "Jamaica",
+      "ko": "자메이카",
+      "my": "Jamaica",
+      "nl": "Jamaica",
+      "pl": "Jamajka",
+      "pt": "Jamaica",
+      "ru": "Ямайка",
+      "sk": "Jamajka",
+      "th": "จาเมกา",
+      "ur": "جمیکا",
+      "vi": "Jamaica",
+      "zh": " 牙买加"
+    },
+    "currency": [
+      "JMD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "81"
+    ],
+    "countryCode": "JP",
+    "countryName": "Japan",
+    "countryNames": {
+      "cs": "Japonsko",
+      "cy": "Japan",
+      "de": "Japan",
+      "en": "Japan",
+      "es": "Japón",
+      "et": "Jaapan",
+      "fi": "Japani",
+      "fr": "Japon",
+      "hr": "Japan",
+      "it": "Giappone",
+      "ja": "日本",
+      "km": "Japan",
+      "ko": "일본",
+      "my": "Japan",
+      "nl": "Japan",
+      "pl": "Japonia",
+      "pt": "Japão",
+      "ru": "Япония",
+      "sk": "Japonsko",
+      "th": "ญี่ปุ่น",
+      "ur": "جاپان",
+      "vi": "Nhật Bản",
+      "zh": "日本"
+    },
+    "currency": [
+      "JPY"
+    ],
+    "region": "Asia",
+    "subregion": "Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "44"
+    ],
+    "countryCode": "JE",
+    "countryName": "Jersey",
+    "countryNames": {
+      "cs": "Jersey",
+      "cy": "Jersey",
+      "de": "Jersey",
+      "en": "Jersey",
+      "es": "Jersey",
+      "et": "Jersey",
+      "fi": "Jersey",
+      "fr": "Jersey",
+      "hr": "Jersey",
+      "it": "Isola di Jersey",
+      "ja": "ジャージー",
+      "km": "Jersey",
+      "ko": "저지 섬",
+      "my": "Jersey",
+      "nl": "Jersey",
+      "pl": "Jersey",
+      "pt": "Jersey",
+      "ru": "Джерси",
+      "sk": "Jersey",
+      "th": "เจอร์ซีย์",
+      "ur": "جرزی",
+      "vi": "Đảo Jersey",
+      "zh": "泽西岛"
+    },
+    "currency": [
+      "GBP"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "962"
+    ],
+    "countryCode": "JO",
+    "countryName": "Jordan",
+    "countryNames": {
+      "cs": "Jordánsko",
+      "cy": "Jordan",
+      "de": "Jordanien",
+      "en": "Jordan",
+      "es": "Jordania",
+      "et": "Jordaania",
+      "fi": "Jordania",
+      "fr": "Jordanie",
+      "hr": "Jordan",
+      "it": "Giordania",
+      "ja": "ヨルダン",
+      "km": "Jordan",
+      "ko": "요르단",
+      "my": "Jordan",
+      "nl": "Jordanië",
+      "pl": "Jordania",
+      "pt": "Jordânia",
+      "ru": "Иордан ия",
+      "sk": "Jordánsko",
+      "th": "จอร์แดน",
+      "ur": "اردن",
+      "vi": "Jordan",
+      "zh": "约旦"
+    },
+    "currency": [
+      "JOD"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "7"
+    ],
+    "countryCode": "KZ",
+    "countryName": "Kazakhstan",
+    "countryNames": {
+      "cs": "Kazachstán",
+      "cy": "Kazakhstan",
+      "de": "Kasachstan",
+      "en": "Kazakhstan",
+      "es": "Kazajistán",
+      "et": "Kasahstan",
+      "fi": "Kazakstan",
+      "fr": "Kazakhstan",
+      "hr": "Kazahstan",
+      "it": "Kazakistan",
+      "ja": "カザフスタン",
+      "km": "Kazakhstan",
+      "ko": "카자흐스탄",
+      "my": "Kazakhstan",
+      "nl": "Kazachstan",
+      "pl": "Kazachstan",
+      "pt": "Cazaquistão",
+      "ru": "Казахстан",
+      "sk": "Kazachstan",
+      "th": "คาซัคสถาน",
+      "ur": "قازقستان",
+      "vi": "Kazakhstan",
+      "zh": "哈 萨克斯坦"
+    },
+    "currency": [
+      "KZT"
+    ],
+    "region": "Asia",
+    "subregion": "Central Asia"
+  },
+  {
+    "callingCode": [
+      "254"
+    ],
+    "countryCode": "KE",
+    "countryName": "Kenya",
+    "countryNames": {
+      "cs": "Keňa",
+      "cy": "Kenya",
+      "de": "Kenia",
+      "en": "Kenya",
+      "es": "Kenia",
+      "et": "Keenia",
+      "fi": "Kenia",
+      "fr": "Kenya",
+      "hr": "Kenija",
+      "it": "Kenya",
+      "ja": "ケニア",
+      "km": "Kenya",
+      "ko": "케냐",
+      "my": "Kenya",
+      "nl": "Kenia",
+      "pl": "Kenia",
+      "pt": "Quénia",
+      "ru": "Кения",
+      "sk": "Keňa",
+      "th": "เคนยา",
+      "ur": "کینیا",
+      "vi": "Kenya",
+      "zh": "肯尼亚"
+    },
+    "currency": [
+      "KES"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "686"
+    ],
+    "countryCode": "KI",
+    "countryName": "Kiribati",
+    "countryNames": {
+      "cs": "Kiribati",
+      "cy": "Kiribati",
+      "de": "Kiribati",
+      "en": "Kiribati",
+      "es": "Kiribati",
+      "et": "Kiribati",
+      "fi": "Kiribati",
+      "fr": "Kiribati",
+      "hr": "Kiribati",
+      "it": "Kiribati",
+      "ja": "キリバス",
+      "km": "Kiribati",
+      "ko": "키리바시",
+      "my": "Kiribati",
+      "nl": "Kiribati",
+      "pl": "Kiribati",
+      "pt": "Kiribati",
+      "ru": "Кирибати",
+      "sk": "Kiribati",
+      "th": "Kiribas",
+      "ur": "کیریباتی",
+      "vi": "Kiribati",
+      "zh": "基里巴斯"
+    },
+    "currency": [
+      "AUD"
+    ],
+    "region": "Oceania",
+    "subregion": "Micronesia"
+  },
+  {
+    "callingCode": [
+      "383"
+    ],
+    "countryCode": "XK",
+    "countryName": "Kosovo",
+    "countryNames": {
+      "cs": "Kosovo",
+      "cy": "Kosovo",
+      "de": "Kosovo",
+      "en": "Kosovo",
+      "es": "Kosovo",
+      "et": "Kosovo",
+      "fi": "Kosovo",
+      "fr": "Kosovo",
+      "hr": "Kosovo",
+      "it": "Kosovo",
+      "ja": "Kosovo",
+      "km": "Kosovo",
+      "ko": "코소보",
+      "my": "Kosovo",
+      "nl": "Kosovo",
+      "pl": "Kosowo",
+      "pt": "Kosovo",
+      "ru": "Республ ика Косово",
+      "sk": "Kosovo",
+      "th": "โคโซโว",
+      "ur": "کوسووہ",
+      "vi": "Kosovo",
+      "zh": "科索沃"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "965"
+    ],
+    "countryCode": "KW",
+    "countryName": "Kuwait",
+    "countryNames": {
+      "cs": "Kuvajt",
+      "cy": "Kuwait",
+      "de": "Kuwait",
+      "en": "Kuwait",
+      "es": "Kuwait",
+      "et": "Kuveit",
+      "fi": "Kuwait",
+      "fr": "Koweït",
+      "hr": "Kuvajt",
+      "it": "Kuwait",
+      "ja": "クウェート",
+      "km": "Kuwait",
+      "ko": "쿠웨이트",
+      "my": "Kuwait",
+      "nl": "Koeweit",
+      "pl": "Kuwejt",
+      "pt": "Kuwait",
+      "ru": "Кувейт",
+      "sk": "Kuvajt",
+      "th": "คูเวต",
+      "ur": "کویت",
+      "vi": "Kuwait",
+      "zh": "科威特"
+    },
+    "currency": [
+      "KWD"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "996"
+    ],
+    "countryCode": "KG",
+    "countryName": "Kyrgyzstan",
+    "countryNames": {
+      "cs": "Kyrgyzstán",
+      "cy": "Kyrgyzstan",
+      "de": "Kirgisistan",
+      "en": "Kyrgyzstan",
+      "es": "Kirguizistán",
+      "et": "Kõrgõzstan",
+      "fi": "Kirgisia",
+      "fr": "Kirghizistan",
+      "hr": "Kirgistan",
+      "it": "Kirghizistan",
+      "ja": "キルギス",
+      "km": "Kyrgyzstan",
+      "ko": "키르기스스탄",
+      "my": "Kyrgyzstan",
+      "nl": "Kirgizië",
+      "pl": "Kirgistan",
+      "pt": "Quirguistão",
+      "ru": "Кирги зия",
+      "sk": "Kirgizsko",
+      "th": "คีร์กีซสถาน",
+      "ur": "کرغیزستان",
+      "vi": "Kyrgyzstan",
+      "zh": "吉尔吉斯斯坦"
+    },
+    "currency": [
+      "KGS"
+    ],
+    "region": "Asia",
+    "subregion": "Central Asia"
+  },
+  {
+    "callingCode": [
+      "856"
+    ],
+    "countryCode": "LA",
+    "countryName": "Laos",
+    "countryNames": {
+      "cs": "Laos",
+      "cy": "Laos",
+      "de": "Laos",
+      "en": "Laos",
+      "es": "Laos",
+      "et": "Laos",
+      "fi": "Laos",
+      "fr": "Laos",
+      "hr": "Laos",
+      "it": "Laos",
+      "ja": "ラ オス人民民主共和国",
+      "km": "Laos",
+      "ko": "라오스",
+      "my": "Laos",
+      "nl": "Laos",
+      "pl": "Laos",
+      "pt": "Laos",
+      "ru": "Ла  ос",
+      "sk": "Laos",
+      "th": "ลาว",
+      "ur": "لاؤس",
+      "vi": "Laos",
+      "zh": "老挝"
+    },
+    "currency": [
+      "LAK"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "371"
+    ],
+    "countryCode": "LV",
+    "countryName": "Latvia",
+    "countryNames": {
+      "cs": "Lotyšsko",
+      "cy": "Latvia",
+      "de": "Lettland",
+      "en": "Latvia",
+      "es": "Letonia",
+      "et": "Läti",
+      "fi": "Latvia",
+      "fr": "Lettonie",
+      "hr": "Latvija",
+      "it": "Lettonia",
+      "ja": "ラトビア",
+      "km": "Latvia",
+      "ko": "라트비아",
+      "my": "Latvia",
+      "nl": "Letland",
+      "pl": "Łotwa",
+      "pt": "Letónia",
+      "ru": "Ла твия",
+      "sk": "Lotyšsko",
+      "th": "ลัตเวีย",
+      "ur": "لٹویا",
+      "vi": "Latvia",
+      "zh": "拉脱维亚"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "961"
+    ],
+    "countryCode": "LB",
+    "countryName": "Lebanon",
+    "countryNames": {
+      "cs": "Libanon",
+      "cy": "Lebanon",
+      "de": "Libanon",
+      "en": "Lebanon",
+      "es": "Líbano",
+      "et": "Liibanon",
+      "fi": "Libanon",
+      "fr": "Liban",
+      "hr": "Libanon",
+      "it": "Libano",
+      "ja": "レバノン",
+      "km": "Lebanon",
+      "ko": "레바논",
+      "my": "Lebanon",
+      "nl": "Libanon",
+      "pl": "Liban",
+      "pt": "Líbano",
+      "ru": "Ливан",
+      "sk": "Libanon",
+      "th": "เลบานอน",
+      "ur": "لبنان",
+      "vi": "Lebanon",
+      "zh": "黎巴嫩"
+    },
+    "currency": [
+      "LBP"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "266"
+    ],
+    "countryCode": "LS",
+    "countryName": "Lesotho",
+    "countryNames": {
+      "cs": "Lesotho",
+      "cy": "Lesotho",
+      "de": "Lesotho",
+      "en": "Lesotho",
+      "es": "Lesotho",
+      "et": "Lesotho",
+      "fi": "Lesotho",
+      "fr": "Lesotho",
+      "hr": "Lesoto",
+      "it": "Lesotho",
+      "ja": "レ ソト",
+      "km": "Lesotho",
+      "ko": "레소토",
+      "my": "Lesotho",
+      "nl": "Lesotho",
+      "pl": "Lesotho",
+      "pt": "Lesoto",
+      "ru": "Лесото",
+      "sk": "Lesotho",
+      "th": "เลโซโท",
+      "ur": "لیسوتھو",
+      "vi": "Lesotho",
+      "zh": "莱索托"
+    },
+    "currency": [
+      "LSL",
+      "ZAR"
+    ],
+    "region": "Africa",
+    "subregion": "Southern Africa"
+  },
+  {
+    "callingCode": [
+      "231"
+    ],
+    "countryCode": "LR",
+    "countryName": "Liberia",
+    "countryNames": {
+      "cs": "Libérie",
+      "cy": "Liberia",
+      "de": "Liberia",
+      "en": "Liberia",
+      "es": "Liberia",
+      "et": "Libeeria",
+      "fi": "Liberia",
+      "fr": "Liberia",
+      "hr": "Liberija",
+      "it": "Liberia",
+      "ja": "リベリア",
+      "km": "Liberia",
+      "ko": "라이베리 아",
+      "my": "Liberia",
+      "nl": "Liberia",
+      "pl": "Liberia",
+      "pt": "Libéria",
+      "ru": "Либерия",
+      "sk": "Libéria",
+      "th": "ไลบีเรีย",
+      "ur": "لائبیریا",
+      "vi": "Liberia",
+      "zh": "利比里亚"
+    },
+    "currency": [
+      "LRD"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "218"
+    ],
+    "countryCode": "LY",
+    "countryName": "Libya",
+    "countryNames": {
+      "cs": "Libye",
+      "cy": "Libya",
+      "de": "Libyen",
+      "en": "Libya",
+      "es": "Libia",
+      "et": "Liibüa",
+      "fi": "Libya",
+      "fr": "Libye",
+      "hr": "Libija",
+      "it": "Libia",
+      "ja": " リビア",
+      "km": "Libya",
+      "ko": "리비아",
+      "my": "Libya",
+      "nl": "Libië",
+      "pl": "Libia",
+      "pt": "Líbia",
+      "ru": "Ливия",
+      "sk": "Líbya",
+      "th": "ลิเบีย",
+      "ur": "لیبیا",
+      "vi": "Libya",
+      "zh": "利比亚"
+    },
+    "currency": [
+      "LYD"
+    ],
+    "region": "Africa",
+    "subregion": "Northern Africa"
+  },
+  {
+    "callingCode": [
+      "423"
+    ],
+    "countryCode": "LI",
+    "countryName": "Liechtenstein",
+    "countryNames": {
+      "cs": "Lichtenštejnsko",
+      "cy": "Liechtenstein",
+      "de": "Liechtenstein",
+      "en": "Liechtenstein",
+      "es": "Liechtenstein",
+      "et": "Liechtenstein",
+      "fi": "Liechenstein",
+      "fr": "Liechtenstein",
+      "hr": "Lihtenštajn",
+      "it": "Liechtenstein",
+      "ja": "リヒ テンシュタイン",
+      "km": "Liechtenstein",
+      "ko": "리히텐슈타인",
+      "my": "Liechtenstein",
+      "nl": "Liechtenstein",
+      "pl": "Liechtenstein",
+      "pt": "Liechtenstein",
+      "ru": "Лихтенштейн",
+      "sk": "Lichtenštajnsko",
+      "th": "Liechtenstein",
+      "ur": "لیختینستائن",
+      "vi": "Liechtenstein",
+      "zh": "列支敦士 登"
+    },
+    "currency": [
+      "CHF"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "370"
+    ],
+    "countryCode": "LT",
+    "countryName": "Lithuania",
+    "countryNames": {
+      "cs": "Litva",
+      "cy": "Lithuania",
+      "de": "Litauen",
+      "en": "Lithuania",
+      "es": "Lituania",
+      "et": "Leedu",
+      "fi": "Liettua",
+      "fr": "Lituanie",
+      "hr": "Litva",
+      "it": "Lituania",
+      "ja": "リトアニア",
+      "km": "Lithuania",
+      "ko": "리투아니아",
+      "my": "Lithuania",
+      "nl": "Litouwen",
+      "pl": "Litwa",
+      "pt": "Lituânia",
+      "ru": "Литва",
+      "sk": "Litva",
+      "th": "ลิทัวเนีย",
+      "ur": "لتھووینیا",
+      "vi": "Lithuania",
+      "zh": "立陶宛"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "352"
+    ],
+    "countryCode": "LU",
+    "countryName": "Luxembourg",
+    "countryNames": {
+      "cs": "Lucembursko",
+      "cy": "Luxembourg",
+      "de": "Luxemburg",
+      "en": "Luxembourg",
+      "es": "Luxemburgo",
+      "et": "Luksemburg",
+      "fi": "Luxemburg",
+      "fr": "Luxembourg",
+      "hr": "Luksemburg",
+      "it": "Lussemburgo",
+      "ja": "ルクセンブルク",
+      "km": "Luxembourg",
+      "ko": "룩셈 부르크",
+      "my": "Luxembourg",
+      "nl": "Luxemburg",
+      "pl": "Luksemburg",
+      "pt": "Luxemburgo",
+      "ru": "Люксембург",
+      "sk": "Luxembursko",
+      "th": "ลักเซมเบิร์ก",
+      "ur": "لکسمبرگ",
+      "vi": "Luxembourg",
+      "zh": "卢森堡"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "853"
+    ],
+    "countryCode": "MO",
+    "countryName": "Macau",
+    "countryNames": {
+      "cs": "Macao",
+      "cy": "Macau",
+      "de": "Macao",
+      "en": "Macau",
+      "es": "Macao",
+      "et": "Macau",
+      "fi": "Macao",
+      "fr": "Macao",
+      "hr": "Makao",
+      "it": "Macao",
+      "ja": "マカオ",
+      "km": "Macau",
+      "ko": "마카오",
+      "my": "Macau",
+      "nl": "Macao",
+      "pl": "Makau",
+      "pt": "Macau",
+      "ru": "Макао",
+      "sk": "Macao",
+      "th": "มาเก๊า",
+      "ur": "مکاؤ",
+      "vi": "Ma Cao",
+      "zh": " 澳门"
+    },
+    "currency": [
+      "MOP"
+    ],
+    "region": "Asia",
+    "subregion": "Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "389"
+    ],
+    "countryCode": "MK",
+    "countryName": "Macedonia",
+    "countryNames": {
+      "cs": "Makedonie",
+      "cy": "Macedonia",
+      "de": "Mazedonien",
+      "en": "Macedonia",
+      "es": "Macedonia",
+      "et": "Makedoonia",
+      "fi": "Makedonia",
+      "fr": "Macédoine",
+      "hr": "Makedonija",
+      "it": "Macedonia",
+      "ja": "マケドニア旧ユーゴスラビア共和国",
+      "km": "Macedonia",
+      "ko": "마케도니아",
+      "my": "Macedonia",
+      "nl": "Macedonië",
+      "pl": "Macedonia",
+      "pt": "Macedónia",
+      "ru": "Республика Македония",
+      "sk": "Macedónsko",
+      "th": "มาซิโดเนีย",
+      "ur": "مقدونیہ",
+      "vi": "Macedonia",
+      "zh": "马其顿"
+    },
+    "currency": [
+      "MKD"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "261"
+    ],
+    "countryCode": "MG",
+    "countryName": "Madagascar",
+    "countryNames": {
+      "cs": "Madagaskar",
+      "cy": "Madagascar",
+      "de": "Madagaskar",
+      "en": "Madagascar",
+      "es": "Madagascar",
+      "et": "Madagaskar",
+      "fi": "Madagaskar",
+      "fr": "Madagascar",
+      "hr": "Madagaskar",
+      "it": "Madagascar",
+      "ja": "マダガスカル",
+      "km": "Madagascar",
+      "ko": "마다가 스카르",
+      "my": "Madagascar",
+      "nl": "Madagaskar",
+      "pl": "Madagaskar",
+      "pt": "Madagáscar",
+      "ru": "Мадагаскар",
+      "sk": "Madagaskar",
+      "th": "มาดากัสการ์",
+      "ur": "مڈغاسکر",
+      "vi": "Madagascar",
+      "zh": " 马达加斯加"
+    },
+    "currency": [
+      "MGA"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "265"
+    ],
+    "countryCode": "MW",
+    "countryName": "Malawi",
+    "countryNames": {
+      "cs": "Malawi",
+      "cy": "Malawi",
+      "de": "Malawi",
+      "en": "Malawi",
+      "es": "Malawi",
+      "et": "Malawi",
+      "fi": "Malawi",
+      "fr": "Malawi",
+      "hr": "Malavi",
+      "it": "Malawi",
+      "ja": "マラウイ",
+      "km": "Malawi",
+      "ko": "말라위",
+      "my": "Malawi",
+      "nl": "Malawi",
+      "pl": "Malawi",
+      "pt": "Malawi",
+      "ru": "Малави",
+      "sk": "Malawi",
+      "th": "มาลาวี",
+      "ur": "ملاوی",
+      "vi": "Malawi",
+      "zh": "马拉维"
+    },
+    "currency": [
+      "MWK"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "60"
+    ],
+    "countryCode": "MY",
+    "countryName": "Malaysia",
+    "countryNames": {
+      "cs": "Malajsie",
+      "cy": "Malaysia",
+      "de": "Malaysia",
+      "en": "Malaysia",
+      "es": "Malasia",
+      "et": "Malaisia",
+      "fi": "Malesia",
+      "fr": "Malaisie",
+      "hr": "Malezija",
+      "it": "Malesia",
+      "ja": "マレーシア",
+      "km": "Malaysia",
+      "ko": "말레이시아",
+      "my": "Malaysia",
+      "nl": "Maleisië",
+      "pl": "Malezja",
+      "pt": "Malásia",
+      "ru": "Малайзия",
+      "sk": "Malajzia",
+      "th": "มาเลเซีย",
+      "ur": "ملائیشیا",
+      "vi": "Malaysia",
+      "zh": "马来西亚"
+    },
+    "currency": [
+      "MYR"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "960"
+    ],
+    "countryCode": "MV",
+    "countryName": "Maldives",
+    "countryNames": {
+      "cs": "Maledivy",
+      "cy": "Maldives",
+      "de": "Malediven",
+      "en": "Maldives",
+      "es": "Maldivas",
+      "et": "Maldiivid",
+      "fi": "Malediivit",
+      "fr": "Maldives",
+      "hr": "Maldivi",
+      "it": "Maldive",
+      "ja": "モルディブ",
+      "km": "Maldives",
+      "ko": "몰디브",
+      "my": "Maldives",
+      "nl": "Maldiven",
+      "pl": "Malediwy",
+      "pt": "Maldivas",
+      "ru": "Мальдивы",
+      "sk": "Maldivy",
+      "th": "มัลดีฟส์",
+      "ur": "مالدیپ",
+      "vi": "Maldives",
+      "zh": "马尔代夫"
+    },
+    "currency": [
+      "MVR"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "223"
+    ],
+    "countryCode": "ML",
+    "countryName": "Mali",
+    "countryNames": {
+      "cs": "Mali",
+      "cy": "Mali",
+      "de": "Mali",
+      "en": "Mali",
+      "es": "Mali",
+      "et": "Mali",
+      "fi": "Mali",
+      "fr": "Mali",
+      "hr": "Mali",
+      "it": "Mali",
+      "ja": "マリ",
+      "km": "Mali",
+      "ko": "말리",
+      "my": "Mali",
+      "nl": "Mali",
+      "pl": "Mali",
+      "pt": "Mali",
+      "ru": "Мали",
+      "sk": "Mali",
+      "th": "มาลี",
+      "ur": "مالی",
+      "vi": "Mali",
+      "zh": "马里"
+    },
+    "currency": [
+      "XOF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "356"
+    ],
+    "countryCode": "MT",
+    "countryName": "Malta",
+    "countryNames": {
+      "cs": "Malta",
+      "cy": "Malta",
+      "de": "Malta",
+      "en": "Malta",
+      "es": "Malta",
+      "et": "Malta",
+      "fi": "Malta",
+      "fr": "Malte",
+      "hr": "Malta",
+      "it": "Malta",
+      "ja": "マルタ",
+      "km": "Malta",
+      "ko": "몰타",
+      "my": "Malta",
+      "nl": "Malta",
+      "pl": "Malta",
+      "pt": "Malta",
+      "ru": "Мальта",
+      "sk": "Malta",
+      "th": "มอลตา",
+      "ur": "مالٹا",
+      "vi": "Malta",
+      "zh": "马耳他"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "692"
+    ],
+    "countryCode": "MH",
+    "countryName": "Marshall Islands",
+    "countryNames": {
+      "cs": "Marshallovy ostrovy",
+      "cy": "Marshall Islands",
+      "de": "Marshallinseln",
+      "en": "Marshall Islands",
+      "es": "Islas Marshall",
+      "et": "Marshalli Saared",
+      "fi": "Marshallinsaaret",
+      "fr": "Îles Marshall",
+      "hr": "Maršalovi Otoci",
+      "it": "Isole Marshall",
+      "ja": "マーシャ ル諸 島",
+      "km": "Marshall Islands",
+      "ko": "마셜 제도",
+      "my": "Marshall Islands",
+      "nl": "Marshalleilanden",
+      "pl": "Wyspy Marshalla",
+      "pt": "Ilhas Marshall",
+      "ru": "Маршалловы Острова",
+      "sk": "Marshallove ostrovy",
+      "th": "หมู่เกาะมาร์แชลล์",
+      "ur": "جزائر مارشل",
+      "vi": "Quần đảo Marshall",
+      "zh": "马绍尔群岛"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Oceania",
+    "subregion": "Micronesia"
+  },
+  {
+    "callingCode": [
+      "596"
+    ],
+    "countryCode": "MQ",
+    "countryName": "Martinique",
+    "countryNames": {
+      "cs": "Martinik",
+      "cy": "Martinique",
+      "de": "Martinique",
+      "en": "Martinique",
+      "es": "Martinica",
+      "et": "Martinique",
+      "fi": "Martinique",
+      "fr": "Martinique",
+      "hr": "Martinique",
+      "it": "Martinica",
+      "ja": "マルティニーク",
+      "km": "Martinique",
+      "ko": "마르티니크",
+      "my": "Martinique",
+      "nl": "Martinique",
+      "pl": "Martynika",
+      "pt": "Martinica",
+      "ru": "Мартиника",
+      "sk": "Martinique",
+      "th": "มาร์ตินีก",
+      "ur": "مارٹینیک",
+      "vi": "Martinique",
+      "zh": "马提尼克"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "222"
+    ],
+    "countryCode": "MR",
+    "countryName": "Mauritania",
+    "countryNames": {
+      "cs": "Mauritánie",
+      "cy": "Mauritania",
+      "de": "Mauretanien",
+      "en": "Mauritania",
+      "es": "Mauritania",
+      "et": "Mauritaania",
+      "fi": "Mauritania",
+      "fr": "Mauritanie",
+      "hr": "Mauritanija",
+      "it": "Mauritania",
+      "ja": "モーリタニア",
+      "km": "Mauritania",
+      "ko": "모리타니",
+      "my": "Mauritania",
+      "nl": "Mauritanië",
+      "pl": "Mauretania",
+      "pt": "Mauritânia",
+      "ru": "Мавритания",
+      "sk": "Mauritánia",
+      "th": "มอริเตเนีย",
+      "ur": "موریتانیہ",
+      "vi": "Mauritania",
+      "zh": "毛里塔尼亚"
+    },
+    "currency": [
+      "MRO"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "230"
+    ],
+    "countryCode": "MU",
+    "countryName": "Mauritius",
+    "countryNames": {
+      "cs": "Mauricius",
+      "cy": "Mauritius",
+      "de": "Mauritius",
+      "en": "Mauritius",
+      "es": "Mauricio",
+      "et": "Mauritius",
+      "fi": "Mauritius",
+      "fr": "Île Maurice",
+      "hr": "Mauricijus",
+      "it": "Mauritius",
+      "ja": "モーリシャス",
+      "km": "Mauritius",
+      "ko": "모리셔스",
+      "my": "Mauritius",
+      "nl": "Mauritius",
+      "pl": "Mauritius",
+      "pt": "Maurício",
+      "ru": "Маврикий",
+      "sk": "Maurícius",
+      "th": "มอริเชียส",
+      "ur": "موریشس",
+      "vi": "Mauritius",
+      "zh": "毛里求斯"
+    },
+    "currency": [
+      "MUR"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "262"
+    ],
+    "countryCode": "YT",
+    "countryName": "Mayotte",
+    "countryNames": {
+      "cs": "Mayotte",
+      "cy": "Mayotte",
+      "de": "Mayotte",
+      "en": "Mayotte",
+      "es": "Mayotte",
+      "et": "Mayotte",
+      "fi": "Mayotte",
+      "fr": "Mayotte",
+      "hr": "Mayotte",
+      "it": "Mayotte",
+      "ja": "マヨ ット",
+      "km": "Mayotte",
+      "ko": "마요트",
+      "my": "Mayotte",
+      "nl": "Mayotte",
+      "pl": "Majotta",
+      "pt": "Mayotte",
+      "ru": "Ма йотта",
+      "sk": "Mayotte",
+      "th": "Mayot",
+      "ur": "مایوٹ",
+      "vi": "Mayotte",
+      "zh": "马约特"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "52"
+    ],
+    "countryCode": "MX",
+    "countryName": "Mexico",
+    "countryNames": {
+      "cs": "Mexiko",
+      "cy": "Mexico",
+      "de": "Mexiko",
+      "en": "Mexico",
+      "es": "México",
+      "et": "Mehhiko",
+      "fi": "Meksiko",
+      "fr": "Mexique",
+      "hr": "Meksiko",
+      "it": "Messico",
+      "ja": "メキ シコ",
+      "km": "Mexico",
+      "ko": "멕시코",
+      "my": "Mexico",
+      "nl": "Mexico",
+      "pl": "Meksyk",
+      "pt": "México",
+      "ru": "Мексика",
+      "sk": "Mexiko",
+      "th": "เม็กซิโก",
+      "ur": "میکسیکو",
+      "vi": "Mexico",
+      "zh": "墨西哥"
+    },
+    "currency": [
+      "MXN"
+    ],
+    "region": "Americas",
+    "subregion": "North America"
+  },
+  {
+    "callingCode": [
+      "691"
+    ],
+    "countryCode": "FM",
+    "countryName": "Micronesia",
+    "countryNames": {
+      "cs": "Mikronésie",
+      "cy": "Micronesia",
+      "de": "Mikronesien",
+      "en": "Micronesia",
+      "es": "Micronesia",
+      "et": "Mikroneesia",
+      "fi": "Mikronesia",
+      "fr": "Micronésie",
+      "hr": "Mikronezija",
+      "it": "Micronesia",
+      "ja": "ミクロネシア連邦",
+      "km": "Micronesia",
+      "ko": "미크로네시 아",
+      "my": "Micronesia",
+      "nl": "Micronesië",
+      "pl": "Mikronezja",
+      "pt": "Micronésia",
+      "ru": "Федеративные Ш та ты Микронезии",
+      "sk": "Mikronézia",
+      "th": "ไมโครนีเซีย",
+      "ur": "مائکرونیشیا",
+      "vi": "Micronesia",
+      "zh": "密 克罗尼西亚"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Oceania",
+    "subregion": "Micronesia"
+  },
+  {
+    "callingCode": [
+      "373"
+    ],
+    "countryCode": "MD",
+    "countryName": "Moldova",
+    "countryNames": {
+      "cs": "Moldavsko",
+      "cy": "Moldova",
+      "de": "Moldawien",
+      "en": "Moldova",
+      "es": "Moldavia",
+      "et": "Moldova",
+      "fi": "Moldova",
+      "fr": "Moldavie",
+      "hr": "Moldova",
+      "it": "Moldavia",
+      "ja": "モ ルドバ共和国",
+      "km": "Moldova",
+      "ko": "몰도바",
+      "my": "Moldova",
+      "nl": "Moldavië",
+      "pl": "Mołdawia",
+      "pt": "Moldávia",
+      "ru": "М олдавия",
+      "sk": "Moldavsko",
+      "th": "มอลโดวา",
+      "ur": "مالدووا",
+      "vi": "Moldova",
+      "zh": "摩尔多瓦"
+    },
+    "currency": [
+      "MDL"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "377"
+    ],
+    "countryCode": "MC",
+    "countryName": "Monaco",
+    "countryNames": {
+      "cs": "Monako",
+      "cy": "Monaco",
+      "de": "Monaco",
+      "en": "Monaco",
+      "es": "Mónaco",
+      "et": "Monaco",
+      "fi": "Monaco",
+      "fr": "Monaco",
+      "hr": "Monako",
+      "it": "Principato di Monaco",
+      "ja": "モナコ",
+      "km": "Monaco",
+      "ko": "모나코",
+      "my": "Monaco",
+      "nl": "Monaco",
+      "pl": "Monako",
+      "pt": "Mónaco",
+      "ru": "Монако",
+      "sk": "Monako",
+      "th": "โมนาโก",
+      "ur": "موناکو",
+      "vi": "Monaco",
+      "zh": " 摩纳哥"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "976"
+    ],
+    "countryCode": "MN",
+    "countryName": "Mongolia",
+    "countryNames": {
+      "cs": "Mongolsko",
+      "cy": "Mongolia",
+      "de": "Mongolei",
+      "en": "Mongolia",
+      "es": "Mongolia",
+      "et": "Mongoolia",
+      "fi": "Mongolia",
+      "fr": "Mongolie",
+      "hr": "Mongolija",
+      "it": "Mongolia",
+      "ja": "モンゴル",
+      "km": "Mongolia",
+      "ko": "몽골국",
+      "my": "Mongolia",
+      "nl": "Mongolië",
+      "pl": "Mongolia",
+      "pt": "Mongólia",
+      "ru": "Монголия",
+      "sk": "Mongolsko",
+      "th": "มองโกเลีย",
+      "ur": "منگولیا",
+      "vi": "Mông Cổ",
+      "zh": "蒙古"
+    },
+    "currency": [
+      "MNT"
+    ],
+    "region": "Asia",
+    "subregion": "Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "382"
+    ],
+    "countryCode": "ME",
+    "countryName": "Montenegro",
+    "countryNames": {
+      "cs": "Černá Hora",
+      "cy": "Montenegro",
+      "de": "Montenegro",
+      "en": "Montenegro",
+      "es": "Montenegro",
+      "et": "Montenegro",
+      "fi": "Montenegro",
+      "fr": "Monténégro",
+      "hr": "Crna Gora",
+      "it": "Montenegro",
+      "ja": "モンテネグロ",
+      "km": "Montenegro",
+      "ko": "몬테네그로",
+      "my": "Montenegro",
+      "nl": "Montenegro",
+      "pl": "Czarnogóra",
+      "pt": "Montenegro",
+      "ru": "Черного рия",
+      "sk": "Čierna Hora",
+      "th": "มอนเตเนโก",
+      "ur": "مونٹینیگرو",
+      "vi": "Montenegro",
+      "zh": "黑山"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "1664"
+    ],
+    "countryCode": "MS",
+    "countryName": "Montserrat",
+    "countryNames": {
+      "cs": "Montserrat",
+      "cy": "Montserrat",
+      "de": "Montserrat",
+      "en": "Montserrat",
+      "es": "Montserrat",
+      "et": "Montserrat",
+      "fi": "Montserrat",
+      "fr": "Montserrat",
+      "hr": "Montserrat",
+      "it": "Montserrat",
+      "ja": "モントセ ラト",
+      "km": "Montserrat",
+      "ko": "몬트세랫",
+      "my": "Montserrat",
+      "nl": "Montserrat",
+      "pl": "Montserrat",
+      "pt": "Montserrat",
+      "ru": "Монтсеррат",
+      "sk": "Montserrat",
+      "th": "มอนต์เซอร์รัต",
+      "ur": "مانٹسریٹ",
+      "vi": "Montserrat",
+      "zh": "蒙特塞拉特"
+    },
+    "currency": [
+      "XCD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "212"
+    ],
+    "countryCode": "MA",
+    "countryName": "Morocco",
+    "countryNames": {
+      "cs": "Maroko",
+      "cy": "Morocco",
+      "de": "Marokko",
+      "en": "Morocco",
+      "es": "Marruecos",
+      "et": "Maroko",
+      "fi": "Marokko",
+      "fr": "Maroc",
+      "hr": "Maroko",
+      "it": "Marocco",
+      "ja": "モロ ッコ",
+      "km": "Morocco",
+      "ko": "모로코",
+      "my": "Morocco",
+      "nl": "Marokko",
+      "pl": "Maroko",
+      "pt": "Marrocos",
+      "ru": "Марокко",
+      "sk": "Maroko",
+      "th": "โมร็อกโก",
+      "ur": "مراکش",
+      "vi": "Morocco",
+      "zh": "摩洛哥"
+    },
+    "currency": [
+      "MAD"
+    ],
+    "region": "Africa",
+    "subregion": "Northern Africa"
+  },
+  {
+    "callingCode": [
+      "258"
+    ],
+    "countryCode": "MZ",
+    "countryName": "Mozambique",
+    "countryNames": {
+      "cs": "Mosambik",
+      "cy": "Mozambique",
+      "de": "Mosambik",
+      "en": "Mozambique",
+      "es": "Mozambique",
+      "et": "Mosambiik",
+      "fi": "Mosambik",
+      "fr": "Mozambique",
+      "hr": "Mozambik",
+      "it": "Mozambico",
+      "ja": "モザンビーク",
+      "km": "Mozambique",
+      "ko": "모잠 비크",
+      "my": "Mozambique",
+      "nl": "Mozambique",
+      "pl": "Mozambik",
+      "pt": "Moçambique",
+      "ru": "Мозамбик",
+      "sk": "Mozambik",
+      "th": "โมซัมบิก",
+      "ur": "موزمبیق",
+      "vi": "Mozambique",
+      "zh": "莫桑比克"
+    },
+    "currency": [
+      "MZN"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "95"
+    ],
+    "countryCode": "MM",
+    "countryName": "Myanmar",
+    "countryNames": {
+      "cs": "Myanmar",
+      "cy": "Myanmar",
+      "de": "Myanmar",
+      "en": "Myanmar",
+      "es": "Myanmar",
+      "et": "Myanmar",
+      "fi": "Myanmar",
+      "fr": "Birmanie",
+      "hr": "Mijanmar",
+      "it": "Birmania",
+      "ja": "ミャンマー",
+      "km": "Myanmar",
+      "ko": "미얀마",
+      "my": "Myanmar",
+      "nl": "Myanmar",
+      "pl": "Mjanma",
+      "pt": "Myanmar",
+      "ru": "Мьянма",
+      "sk": "Mjanmarsko",
+      "th": "พม่า",
+      "ur": "میانمار",
+      "vi": "Miến Điện",
+      "zh": " 缅甸"
+    },
+    "currency": [
+      "MMK"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "264"
+    ],
+    "countryCode": "NA",
+    "countryName": "Namibia",
+    "countryNames": {
+      "cs": "Namibie",
+      "cy": "Namibia",
+      "de": "Namibia",
+      "en": "Namibia",
+      "es": "Namibia",
+      "et": "Namiibia",
+      "fi": "Namibia",
+      "fr": "Namibie",
+      "hr": "Namibija",
+      "it": "Namibia",
+      "ja": "ナミビア",
+      "km": "Namibia",
+      "ko": "나미비아",
+      "my": "Namibia",
+      "nl": "Namibië",
+      "pl": "Namibia",
+      "pt": "Namíbia",
+      "ru": "Намибия",
+      "sk": "Namíbia",
+      "th": "นามิเบีย",
+      "ur": "نمیبیا",
+      "vi": "Namibia",
+      "zh": "纳米比 亚"
+    },
+    "currency": [
+      "NAD",
+      "ZAR"
+    ],
+    "region": "Africa",
+    "subregion": "Southern Africa"
+  },
+  {
+    "callingCode": [
+      "674"
+    ],
+    "countryCode": "NR",
+    "countryName": "Nauru",
+    "countryNames": {
+      "cs": "Nauru",
+      "cy": "Nauru",
+      "de": "Nauru",
+      "en": "Nauru",
+      "es": "Nauru",
+      "et": "Nauru",
+      "fi": "Nauru",
+      "fr": "Nauru",
+      "hr": "Nauru",
+      "it": "Nauru",
+      "ja": "ナウル",
+      "km": "Nauru",
+      "ko": "나우 루",
+      "my": "Nauru",
+      "nl": "Nauru",
+      "pl": "Nauru",
+      "pt": "Nauru",
+      "ru": "Науру",
+      "sk": "Nauru",
+      "th": "นาอูรู",
+      "ur": "ناورو",
+      "vi": "Nauru",
+      "zh": "瑙鲁"
+    },
+    "currency": [
+      "AUD"
+    ],
+    "region": "Oceania",
+    "subregion": "Micronesia"
+  },
+  {
+    "callingCode": [
+      "977"
+    ],
+    "countryCode": "NP",
+    "countryName": "Nepal",
+    "countryNames": {
+      "cs": "Nepál",
+      "cy": "Nepal",
+      "de": "Nepal",
+      "en": "Nepal",
+      "es": "Nepal",
+      "et": "Nepal",
+      "fi": "Nepal",
+      "fr": "Népal",
+      "hr": "Nepal",
+      "it": "Nepal",
+      "ja": "ネパール",
+      "km": "Nepal",
+      "ko": "네팔",
+      "my": "Nepal",
+      "nl": "Nepal",
+      "pl": "Nepal",
+      "pt": "Nepal",
+      "ru": "Н еп ал",
+      "sk": "Nepál",
+      "th": "เนปาล",
+      "ur": "نیپال",
+      "vi": "Nepal",
+      "zh": "尼泊尔"
+    },
+    "currency": [
+      "NPR"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "31"
+    ],
+    "countryCode": "NL",
+    "countryName": "Netherlands",
+    "countryNames": {
+      "cs": "Nizozemsko",
+      "cy": "Netherlands",
+      "de": "Niederlande",
+      "en": "Netherlands",
+      "es": "Países Bajos",
+      "et": "Holland",
+      "fi": "Alankomaat",
+      "fr": "Pays-Bas",
+      "hr": "Nizozemska",
+      "it": "Paesi Bassi",
+      "ja": "オランダ",
+      "km": "Netherlands",
+      "ko": "네덜란 드",
+      "my": "Netherlands",
+      "nl": "Nederland",
+      "pl": "Holandia",
+      "pt": "Holanda",
+      "ru": "Нидерлан ды",
+      "sk": "Holansko",
+      "th": "เนเธอร์แลนด์",
+      "ur": "نیدرلینڈز",
+      "vi": "Hà Lan",
+      "zh": "荷兰"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "687"
+    ],
+    "countryCode": "NC",
+    "countryName": "New Caledonia",
+    "countryNames": {
+      "cs": "Nová Kaledonie",
+      "cy": "New Caledonia",
+      "de": "Neukaledonien",
+      "en": "New Caledonia",
+      "es": "Nueva Caledonia",
+      "et": "Uus-Kaledoonia",
+      "fi": "Uusi-Kaledonia",
+      "fr": "Nouvelle-Calédonie",
+      "hr": "Nova Kaledonija",
+      "it": "Nuova Caledonia",
+      "ja": "ニューカレドニア",
+      "km": "New Caledonia",
+      "ko": "누벨칼레도니",
+      "my": "New Caledonia",
+      "nl": "Nieuw-Caledonië",
+      "pl": "Nowa Kaledonia",
+      "pt": "Nova Caledónia",
+      "ru": "Новая Каледони я",
+      "sk": "Nová Kaledónia",
+      "th": "นิวแคลิโดเนีย",
+      "ur": "نیو کیلیڈونیا",
+      "vi": "New Caledonia",
+      "zh": "新喀里多尼亚"
+    },
+    "currency": [
+      "XPF"
+    ],
+    "region": "Oceania",
+    "subregion": "Melanesia"
+  },
+  {
+    "callingCode": [
+      "64"
+    ],
+    "countryCode": "NZ",
+    "countryName": "New Zealand",
+    "countryNames": {
+      "cs": "Nový Zéland",
+      "cy": "New Zealand",
+      "de": "Neuseeland",
+      "en": "New Zealand",
+      "es": "Nueva Zelanda",
+      "et": "Uus-Meremaa",
+      "fi": "Uusi-Seelanti",
+      "fr": "Nouvelle-Zélande",
+      "hr": "Novi Zeland",
+      "it": "Nuova Zelanda",
+      "ja": "ニュージーランド",
+      "km": "New Zealand",
+      "ko": "뉴질랜드",
+      "my": "New Zealand",
+      "nl": "Nieuw-Zeeland",
+      "pl": "Nowa Zelandia",
+      "pt": "Nova Zelândia",
+      "ru": "Новая Зеландия",
+      "sk": "Nový Zéland",
+      "th": "นิวซีแลนด์",
+      "ur": "نیوزی لینڈ",
+      "vi": "New Zealand",
+      "zh": "新西兰"
+    },
+    "currency": [
+      "NZD"
+    ],
+    "region": "Oceania",
+    "subregion": "Australia and New Zealand"
+  },
+  {
+    "callingCode": [
+      "505"
+    ],
+    "countryCode": "NI",
+    "countryName": "Nicaragua",
+    "countryNames": {
+      "cs": "Nikaragua",
+      "cy": "Nicaragua",
+      "de": "Nicaragua",
+      "en": "Nicaragua",
+      "es": "Nicaragua",
+      "et": "Nicaragua",
+      "fi": "Nicaragua",
+      "fr": "Nicaragua",
+      "hr": "Nikaragva",
+      "it": "Nicaragua",
+      "ja": "ニカラグア",
+      "km": "Nicaragua",
+      "ko": "니카라과",
+      "my": "Nicaragua",
+      "nl": "Nicaragua",
+      "pl": "Nikaragua",
+      "pt": "Nicarágua",
+      "ru": "Никарагуа",
+      "sk": "Nikaragua",
+      "th": "นิการากัว",
+      "ur": "نکاراگوا",
+      "vi": "Nicaragua",
+      "zh": " 尼加拉瓜"
+    },
+    "currency": [
+      "NIO"
+    ],
+    "region": "Americas",
+    "subregion": "Central America"
+  },
+  {
+    "callingCode": [
+      "227"
+    ],
+    "countryCode": "NE",
+    "countryName": "Niger",
+    "countryNames": {
+      "cs": "Niger",
+      "cy": "Niger",
+      "de": "Niger",
+      "en": "Niger",
+      "es": "Níger",
+      "et": "Niger",
+      "fi": "Niger",
+      "fr": "Niger",
+      "hr": "Niger",
+      "it": "Niger",
+      "ja": "ニジェール",
+      "km": "Niger",
+      "ko": "니제르",
+      "my": "Niger",
+      "nl": "Niger",
+      "pl": "Niger",
+      "pt": "Níger",
+      "ru": "Нигер",
+      "sk": "Niger",
+      "th": "ไนเจอร์",
+      "ur": "نائجر",
+      "vi": "Niger",
+      "zh": "尼日尔"
+    },
+    "currency": [
+      "XOF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "234"
+    ],
+    "countryCode": "NG",
+    "countryName": "Nigeria",
+    "countryNames": {
+      "cs": "Nigérie",
+      "cy": "Nigeria",
+      "de": "Nigeria",
+      "en": "Nigeria",
+      "es": "Nigeria",
+      "et": "Nigeeria",
+      "fi": "Nigeria",
+      "fr": "Nigéria",
+      "hr": "Nigerija",
+      "it": "Nigeria",
+      "ja": " ナイジェリア",
+      "km": "Nigeria",
+      "ko": "나이지리아",
+      "my": "Nigeria",
+      "nl": "Nigeria",
+      "pl": "Nigeria",
+      "pt": "Nigéria",
+      "ru": "Нигерия",
+      "sk": "Nigéria",
+      "th": "ไนจีเรีย",
+      "ur": "نائجیریا",
+      "vi": "Nigeria",
+      "zh": "尼日利亚"
+    },
+    "currency": [
+      "NGN"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "683"
+    ],
+    "countryCode": "NU",
+    "countryName": "Niue",
+    "countryNames": {
+      "cs": "Niue",
+      "cy": "Niue",
+      "de": "Niue",
+      "en": "Niue",
+      "es": "Niue",
+      "et": "Niue",
+      "fi": "Niue",
+      "fr": "Niue",
+      "hr": "Niue",
+      "it": "Niue",
+      "ja": "ニウエ",
+      "km": "Niue",
+      "ko": "니우에",
+      "my": "Niue",
+      "nl": "Niue",
+      "pl": "Niue",
+      "pt": "Niue",
+      "ru": "Ниуэ",
+      "sk": "Niue",
+      "th": "Newer",
+      "ur": "نیووے",
+      "vi": "Niue",
+      "zh": "纽 埃"
+    },
+    "currency": [
+      "NZD"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "672"
+    ],
+    "countryCode": "NF",
+    "countryName": "Norfolk Island",
+    "countryNames": {
+      "cs": "Norfolk",
+      "cy": "Norfolk Island",
+      "de": "Norfolkinsel",
+      "en": "Norfolk Island",
+      "es": "Isla de Norfolk",
+      "et": "Norfolk",
+      "fi": "Norfolkinsaari",
+      "fr": "Île Norfolk",
+      "hr": "Otok Norfolk",
+      "it": "Isola Norfolk",
+      "ja": "ノーフォーク島",
+      "km": "Norfolk Island",
+      "ko": "노퍽 섬",
+      "my": "Norfolk Island",
+      "nl": "Norfolkeiland",
+      "pl": "Wyspa Norfolk",
+      "pt": "Ilha Norfolk",
+      "ru": "Норфолк",
+      "sk": "Norfolk",
+      "th": "เกาะนอร์ฟอล์ก",
+      "ur": "جزیرہ نورفک",
+      "vi": "Đảo Norfolk",
+      "zh": "诺福克岛"
+    },
+    "currency": [
+      "AUD"
+    ],
+    "region": "Oceania",
+    "subregion": "Australia and New Zealand"
+  },
+  {
+    "callingCode": [
+      "850"
+    ],
+    "countryCode": "KP",
+    "countryName": "North Korea",
+    "countryNames": {
+      "cs": "Severní Korea",
+      "cy": "North Korea",
+      "de": "Nordkorea",
+      "en": "North Korea",
+      "es": "Corea del Norte",
+      "et": "Põhja-Korea",
+      "fi": "Pohjois-Korea",
+      "fr": "Corée du Nord",
+      "hr": "Sjeverna Koreja",
+      "it": "Corea del Nord",
+      "ja": "朝鮮民 主主義人民共和国",
+      "km": "North Korea",
+      "ko": "조선",
+      "my": "North Korea",
+      "nl": "Noord-Korea",
+      "pl": "Korea Północna",
+      "pt": "Coreia do Norte",
+      "ru": "Се верная Корея",
+      "sk": "Kórejská ľudovodemokratická republika (KĽR, Severná Kórea)",
+      "th": "เกาหลีเหนือ",
+      "ur": "شمالی کوریا",
+      "vi": "Bắc Triều Tiên",
+      "zh": "朝鲜"
+    },
+    "currency": [
+      "KPW"
+    ],
+    "region": "Asia",
+    "subregion": "Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "1670"
+    ],
+    "countryCode": "MP",
+    "countryName": "Northern Mariana Islands",
+    "countryNames": {
+      "cs": "Severní Mariany",
+      "cy": "Northern Mariana Islands",
+      "de": "Nördliche Marianen",
+      "en": "Northern Mariana Islands",
+      "es": "Islas Marianas del Norte",
+      "et": "Põhja-Mariaanid",
+      "fi": "Pohjois-Mariaanit",
+      "fr": "Îles Mariannes du Nord",
+      "hr": "Sjevernomarijanski otoci",
+      "it": "Isole Marianne Settentrionali",
+      "ja": "北マリアナ諸島",
+      "km": "Northern Mariana Islands",
+      "ko": "북마리아나 제도",
+      "my": "Northern Mariana Islands",
+      "nl": "Noordelijke Marianeneilanden",
+      "pl": "Mariany Północne",
+      "pt": "Marianas Setentrionais",
+      "ru": "Северные Марианские острова",
+      "sk": "Severné Mariány",
+      "th": "หมู่เกาะนอร์เทิร์นมาเรียนา",
+      "ur": "جزائر شمالی ماریانا",
+      "vi": "Quần đảo Bắc Mariana",
+      "zh": "北马里亚纳群岛"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Oceania",
+    "subregion": "Micronesia"
+  },
+  {
+    "callingCode": [
+      "47"
+    ],
+    "countryCode": "NO",
+    "countryName": "Norway",
+    "countryNames": {
+      "cs": "Norsko",
+      "cy": "Norway",
+      "de": "Norwegen",
+      "en": "Norway",
+      "es": "Noruega",
+      "et": "Norra",
+      "fi": "Norja",
+      "fr": "Norvège",
+      "hr": "Norveška",
+      "it": "Norvegia",
+      "ja": "ノルウェー",
+      "km": "Norway",
+      "ko": "노르웨이",
+      "my": "Norway",
+      "nl": "Noorwegen",
+      "pl": "Norwegia",
+      "pt": "Noruega",
+      "ru": "Норвегия",
+      "sk": "Nórsko",
+      "th": "นอร์เวย์",
+      "ur": "ناروے",
+      "vi": "Norway",
+      "zh": "挪威"
+    },
+    "currency": [
+      "NOK"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "968"
+    ],
+    "countryCode": "OM",
+    "countryName": "Oman",
+    "countryNames": {
+      "cs": "Omán",
+      "cy": "Oman",
+      "de": "Oman",
+      "en": "Oman",
+      "es": "Omán",
+      "et": "Omaan",
+      "fi": "Oman",
+      "fr": "Oman",
+      "hr": "Oman",
+      "it": "oman",
+      "ja": "オマーン",
+      "km": "Oman",
+      "ko": "오만",
+      "my": "Oman",
+      "nl": "Oman",
+      "pl": "Oman",
+      "pt": "Omã",
+      "ru": "Оман",
+      "sk": "Omán",
+      "th": "โอมาน",
+      "ur": "عمان",
+      "vi": "Oman",
+      "zh": "阿曼"
+    },
+    "currency": [
+      "OMR"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "92"
+    ],
+    "countryCode": "PK",
+    "countryName": "Pakistan",
+    "countryNames": {
+      "cs": "Pákistán",
+      "cy": "Pakistan",
+      "de": "Pakistan",
+      "en": "Pakistan",
+      "es": "Pakistán",
+      "et": "Pakistan",
+      "fi": "Pakistan",
+      "fr": "Pakistan",
+      "hr": "Pakistan",
+      "it": "Pakistan",
+      "ja": " パキスタン",
+      "km": "Pakistan",
+      "ko": "파키스탄",
+      "my": "Pakistan",
+      "nl": "Pakistan",
+      "pl": "Pakistan",
+      "pt": "Paquistão",
+      "ru": "Пак истан",
+      "sk": "Pakistan",
+      "th": "ปากีสถาน",
+      "ur": "پاکستان",
+      "vi": "Pakistan",
+      "zh": "巴基斯坦"
+    },
+    "currency": [
+      "PKR"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "680"
+    ],
+    "countryCode": "PW",
+    "countryName": "Palau",
+    "countryNames": {
+      "cs": "Palau",
+      "cy": "Palau",
+      "de": "Palau",
+      "en": "Palau",
+      "es": "Palau",
+      "et": "Belau",
+      "fi": "Palau",
+      "fr": "Palaos (Palau)",
+      "hr": "Palau",
+      "it": "Palau",
+      "ja": "パラオ",
+      "km": "Palau",
+      "ko": "팔라우",
+      "my": "Palau",
+      "nl": "Palau",
+      "pl": "Palau",
+      "pt": "Palau",
+      "ru": "Палау",
+      "sk": "Palau",
+      "th": "ปาเลา",
+      "ur": "پلاؤ",
+      "vi": "Palau",
+      "zh": "帕劳"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Oceania",
+    "subregion": "Micronesia"
+  },
+  {
+    "callingCode": [
+      "970"
+    ],
+    "countryCode": "PS",
+    "countryName": "Palestine",
+    "countryNames": {
+      "cs": "Palestina",
+      "cy": "Palestine",
+      "de": "Palästina",
+      "en": "Palestine",
+      "es": "Palestina",
+      "et": "Palestiina",
+      "fi": "Palestiina",
+      "fr": "Palestine",
+      "hr": "Palestina",
+      "it": "Palestina",
+      "ja": "パレスチナ",
+      "km": "Palestine",
+      "ko": "팔레스타인",
+      "my": "Palestine",
+      "nl": "Palestijnse gebieden",
+      "pl": "Palestyna",
+      "pt": "Palestina",
+      "ru": "Палестина",
+      "sk": "Palestína",
+      "th": "ปาเลสไตน์",
+      "ur": "فلسطین",
+      "vi": "Palestine",
+      "zh": " 巴勒斯坦"
+    },
+    "currency": [
+      "ILS"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "507"
+    ],
+    "countryCode": "PA",
+    "countryName": "Panama",
+    "countryNames": {
+      "cs": "Panama",
+      "cy": "Panama",
+      "de": "Panama",
+      "en": "Panama",
+      "es": "Panamá",
+      "et": "Panama",
+      "fi": "Panama",
+      "fr": "Panama",
+      "hr": "Panama",
+      "it": "Panama",
+      "ja": "パナマ",
+      "km": "Panama",
+      "ko": "파나마",
+      "my": "Panama",
+      "nl": "Panama",
+      "pl": "Panama",
+      "pt": "Panamá",
+      "ru": "Панама",
+      "sk": "Panama",
+      "th": "ปานามา",
+      "ur": "پاناما",
+      "vi": "Panama",
+      "zh": "巴拿马"
+    },
+    "currency": [
+      "PAB",
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "Central America"
+  },
+  {
+    "callingCode": [
+      "675"
+    ],
+    "countryCode": "PG",
+    "countryName": "Papua New Guinea",
+    "countryNames": {
+      "cs": "Papua-Nová Guinea",
+      "cy": "Papua New Guinea",
+      "de": "Papua-Neuguinea",
+      "en": "Papua New Guinea",
+      "es": "Papúa Nueva Guinea",
+      "et": "Paapua Uus-Guinea",
+      "fi": "Papua-Uusi-Guinea",
+      "fr": "Papouasie-Nouvelle-Guinée",
+      "hr": "Papua Nova Gvineja",
+      "it": "Papua Nuova Guinea",
+      "ja": "パプアニューギニア",
+      "km": "Papua New Guinea",
+      "ko": "파푸아뉴기니",
+      "my": "Papua New Guinea",
+      "nl": "Papoea-Nieuw-Guinea",
+      "pl": "Papua-Nowa Gwinea",
+      "pt": "Papua Nova Guiné",
+      "ru": "Папуа —  Новая Гв инея",
+      "sk": "Papua-Nová Guinea",
+      "th": "ปาปัวนิวกินี",
+      "ur": "پاپوا نیو گنی",
+      "vi": "Papua New Guinea",
+      "zh": "巴布亚新几内亚"
+    },
+    "currency": [
+      "PGK"
+    ],
+    "region": "Oceania",
+    "subregion": "Melanesia"
+  },
+  {
+    "callingCode": [
+      "595"
+    ],
+    "countryCode": "PY",
+    "countryName": "Paraguay",
+    "countryNames": {
+      "cs": "Paraguay",
+      "cy": "Paraguay",
+      "de": "Paraguay",
+      "en": "Paraguay",
+      "es": "Paraguay",
+      "et": "Paraguay",
+      "fi": "Paraguay",
+      "fr": "Paraguay",
+      "hr": "Paragvaj",
+      "it": "Paraguay",
+      "ja": "パラグアイ",
+      "km": "Paraguay",
+      "ko": "파라과이",
+      "my": "Paraguay",
+      "nl": "Paraguay",
+      "pl": "Paragwaj",
+      "pt": "Paraguai",
+      "ru": "Парагвай",
+      "sk": "Paraguaj",
+      "th": "ปารากวัย",
+      "ur": "پیراگوئے",
+      "vi": "Paraguay",
+      "zh": "巴拉圭"
+    },
+    "currency": [
+      "PYG"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "51"
+    ],
+    "countryCode": "PE",
+    "countryName": "Peru",
+    "countryNames": {
+      "cs": "Peru",
+      "cy": "Peru",
+      "de": "Peru",
+      "en": "Peru",
+      "es": "Perú",
+      "et": "Peruu",
+      "fi": "Peru",
+      "fr": "Pérou",
+      "hr": "Peru",
+      "it": "Perù",
+      "ja": "ペルー",
+      "km": "Peru",
+      "ko": "페루",
+      "my": "Peru",
+      "nl": "Peru",
+      "pl": "Peru",
+      "pt": "Perú",
+      "ru": "Перу",
+      "sk": "Peru",
+      "th": "เปรู",
+      "ur": "پیرو",
+      "vi": "Peru",
+      "zh": "秘鲁"
+    },
+    "currency": [
+      "PEN"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "63"
+    ],
+    "countryCode": "PH",
+    "countryName": "Philippines",
+    "countryNames": {
+      "cs": "Filipíny",
+      "cy": "Philippines",
+      "de": "Philippinen",
+      "en": "Philippines",
+      "es": "Filipinas",
+      "et": "Filipiinid",
+      "fi": "Filippiinit",
+      "fr": "Philippines",
+      "hr": "Filipini",
+      "it": "Filippine",
+      "ja": "フィリピン",
+      "km": "Philippines",
+      "ko": "필 리핀",
+      "my": "Philippines",
+      "nl": "Filipijnen",
+      "pl": "Filipiny",
+      "pt": "Filipinas",
+      "ru": "Филиппины",
+      "sk": "Filipíny",
+      "th": "ฟิลิปปินส์",
+      "ur": "فلپائن",
+      "vi": "Philippines",
+      "zh": "菲律宾"
+    },
+    "currency": [
+      "PHP"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "64"
+    ],
+    "countryCode": "PN",
+    "countryName": "Pitcairn Islands",
+    "countryNames": {
+      "cs": "Pitcairnovy ostrovy",
+      "cy": "Pitcairn Islands",
+      "de": "Pitcairninseln",
+      "en": "Pitcairn Islands",
+      "es": "Islas Pitcairn",
+      "et": "Pitcairn",
+      "fi": "Pitcairn",
+      "fr": "Îles Pitcairn",
+      "hr": "Pitcairnovo otočje",
+      "it": "Isole Pitcairn",
+      "ja": "ピトケアン",
+      "km": "Pitcairn Islands",
+      "ko": "핏케언 제도",
+      "my": "Pitcairn Islands",
+      "nl": "Pitcairneilanden",
+      "pl": "Pitcairn",
+      "pt": "Ilhas Pitcairn",
+      "ru": "Острова Питкэрн",
+      "sk": "Pitcairnove ostrovy",
+      "th": "หมู่เกาะพิตแคร์น",
+      "ur": "جزائر پٹکیرن",
+      "vi": "Quần đảo Pitcairn",
+      "zh": "皮特凯恩群岛"
+    },
+    "currency": [
+      "NZD"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "48"
+    ],
+    "countryCode": "PL",
+    "countryName": "Poland",
+    "countryNames": {
+      "cs": "Polsko",
+      "cy": "Poland",
+      "de": "Polen",
+      "en": "Poland",
+      "es": "Polonia",
+      "et": "Poola",
+      "fi": "Puola",
+      "fr": "Pologne",
+      "hr": "Poljska",
+      "it": "Polonia",
+      "ja": "ポーランド",
+      "km": "Poland",
+      "ko": "폴란드",
+      "my": "Poland",
+      "nl": "Polen",
+      "pl": "Polska",
+      "pt": "Polónia",
+      "ru": "Поль ша",
+      "sk": "Poľsko",
+      "th": "โปแลนด์",
+      "ur": "پولینڈ",
+      "vi": "Ba Lan",
+      "zh": "波兰"
+    },
+    "currency": [
+      "PLN"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "351"
+    ],
+    "countryCode": "PT",
+    "countryName": "Portugal",
+    "countryNames": {
+      "cs": "Portugalsko",
+      "cy": "Portugal",
+      "de": "Portugal",
+      "en": "Portugal",
+      "es": "Portugal",
+      "et": "Portugal",
+      "fi": "Portugali",
+      "fr": "Portugal",
+      "hr": "Portugal",
+      "it": "Portogallo",
+      "ja": "ポルトガル",
+      "km": "Portugal",
+      "ko": "포르투갈",
+      "my": "Portugal",
+      "nl": "Portugal",
+      "pl": "Portugalia",
+      "pt": "Portugal",
+      "ru": "Пор тугалия",
+      "sk": "Portugalsko",
+      "th": "โปรตุเกส",
+      "ur": "پرتگال",
+      "vi": "Bồ Đào Nha",
+      "zh": "葡萄 牙"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "1787",
+      "1939"
+    ],
+    "countryCode": "PR",
+    "countryName": "Puerto Rico",
+    "countryNames": {
+      "cs": "Portoriko",
+      "cy": "Puerto Rico",
+      "de": "Puerto Rico",
+      "en": "Puerto Rico",
+      "es": "Puerto Rico",
+      "et": "Puerto Rico",
+      "fi": "Puerto Rico",
+      "fr": "Porto Rico",
+      "hr": "Portoriko",
+      "it": "Porto Rico",
+      "ja": "プエルトリコ",
+      "km": "Puerto Rico",
+      "ko": "푸에르토 리코",
+      "my": "Puerto Rico",
+      "nl": "Puerto Rico",
+      "pl": "Portoryko",
+      "pt": "Porto Rico",
+      "ru": "Пуэрто-Рико",
+      "sk": "Portoriko",
+      "th": "เปอร์โตริโก",
+      "ur": "پورٹو ریکو",
+      "vi": "Puerto Rico",
+      "zh": "波多黎各"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "974"
+    ],
+    "countryCode": "QA",
+    "countryName": "Qatar",
+    "countryNames": {
+      "cs": "Katar",
+      "cy": "Qatar",
+      "de": "Katar",
+      "en": "Qatar",
+      "es": "Catar",
+      "et": "Katar",
+      "fi": "Qatar",
+      "fr": "Qatar",
+      "hr": "Katar",
+      "it": "Qatar",
+      "ja": "カタール",
+      "km": "Qatar",
+      "ko": "카타르",
+      "my": "Qatar",
+      "nl": "Qatar",
+      "pl": "Katar",
+      "pt": "Catar",
+      "ru": " Катар",
+      "sk": "Katar",
+      "th": "กาตาร์",
+      "ur": "قطر",
+      "vi": "Qatar",
+      "zh": "卡塔尔"
+    },
+    "currency": [
+      "QAR"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "242"
+    ],
+    "countryCode": "CG",
+    "countryName": "Republic of the Congo",
+    "countryNames": {
+      "cs": "Kongo",
+      "cy": "Gweriniaeth y Congo",
+      "de": "Kongo",
+      "en": "Republic of the Congo",
+      "es": "Congo",
+      "et": "Kongo Vabariik",
+      "fi": "Kongo-Brazzaville",
+      "fr": "Congo",
+      "hr": "Kongo",
+      "it": "Congo",
+      "ja": "コンゴ共和国",
+      "km": "Republic of the Congo",
+      "ko": "콩고",
+      "my": "Republic of the Congo",
+      "nl": "Congo",
+      "pl": "Kongo",
+      "pt": "Congo",
+      "ru": "Республика Конго",
+      "sk": "Kongo",
+      "th": "คองโก",
+      "ur": "جمہوریہ کانگو",
+      "vi": "Cộng hòa Congo",
+      "zh": "刚果"
+    },
+    "currency": [
+      "XAF"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "40"
+    ],
+    "countryCode": "RO",
+    "countryName": "Romania",
+    "countryNames": {
+      "cs": "Rumunsko",
+      "cy": "Romania",
+      "de": "Rumänien",
+      "en": "Romania",
+      "es": "Rumania",
+      "et": "Rumeenia",
+      "fi": "Romania",
+      "fr": "Roumanie",
+      "hr": "Rumunjska",
+      "it": "Romania",
+      "ja": "ルーマニ ア",
+      "km": "Romania",
+      "ko": "루마니아",
+      "my": "Romania",
+      "nl": "Roemenië",
+      "pl": "Rumunia",
+      "pt": "Roménia",
+      "ru": "Румыния",
+      "sk": "Rumunsko",
+      "th": "โรมาเนีย",
+      "ur": "رومانیہ",
+      "vi": "Romania",
+      "zh": "罗马尼亚"
+    },
+    "currency": [
+      "RON"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "7"
+    ],
+    "countryCode": "RU",
+    "countryName": "Russia",
+    "countryNames": {
+      "cs": "Rusko",
+      "cy": "Russia",
+      "de": "Russland",
+      "en": "Russia",
+      "es": "Rusia",
+      "et": "Venemaa",
+      "fi": "Venäjä",
+      "fr": "Russie",
+      "hr": "Rusija",
+      "it": "Russia",
+      "ja": "ロシア連邦",
+      "km": "Russia",
+      "ko": "러시아",
+      "my": "Russia",
+      "nl": "Rusland",
+      "pl": "Rosja",
+      "pt": "Rússia",
+      "ru": "Россия",
+      "sk": "Rusko",
+      "th": "รัสเซีย",
+      "ur": "روس",
+      "vi": "Nước Nga",
+      "zh": "俄罗斯"
+    },
+    "currency": [
+      "RUB"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "250"
+    ],
+    "countryCode": "RW",
+    "countryName": "Rwanda",
+    "countryNames": {
+      "cs": "Rwanda",
+      "cy": "Rwanda",
+      "de": "Ruanda",
+      "en": "Rwanda",
+      "es": "Ruanda",
+      "et": "Rwanda",
+      "fi": "Ruanda",
+      "fr": "Rwanda",
+      "hr": "Ruanda",
+      "it": "Ruanda",
+      "ja": "ルワンダ",
+      "km": "Rwanda",
+      "ko": "르완다",
+      "my": "Rwanda",
+      "nl": "Rwanda",
+      "pl": "Rwanda",
+      "pt": "Ruanda",
+      "ru": "Руанда",
+      "sk": "Rwanda",
+      "th": "รวันดา",
+      "ur": "روانڈا",
+      "vi": "Rwanda",
+      "zh": "卢旺达"
+    },
+    "currency": [
+      "RWF"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "262"
+    ],
+    "countryCode": "RE",
+    "countryName": "Réunion",
+    "countryNames": {
+      "cs": "Réunion",
+      "cy": "Réunion",
+      "de": "Réunion",
+      "en": "Réunion",
+      "es": "Reunión",
+      "et": "Réunion",
+      "fi": "Réunion",
+      "fr": "Réunion",
+      "hr": "Réunion",
+      "it": "Riunione",
+      "ja": "レユニオン",
+      "km": "Réunion",
+      "ko": "레 위니옹",
+      "my": "Réunion",
+      "nl": "Réunion",
+      "pl": "Reunion",
+      "pt": "Reunião",
+      "ru": "Реюньон",
+      "sk": "Réunion",
+      "th": "เกาะเรอูนียง",
+      "ur": "رے یونیوں",
+      "vi": "Đảo Réunion",
+      "zh": "留尼旺岛"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "590"
+    ],
+    "countryCode": "BL",
+    "countryName": "Saint Barthélemy",
+    "countryNames": {
+      "cs": "Svatý Bartoloměj",
+      "cy": "Saint Barthélemy",
+      "de": "Saint-Barthélemy",
+      "en": "Saint Barthélemy",
+      "es": "San Bartolomé",
+      "et": "Saint-Barthélemy",
+      "fi": "Saint-Barthélemy",
+      "fr": "Saint-Barthélemy",
+      "hr": "Saint Barthélemy",
+      "it": "Antille Francesi",
+      "ja": "サン・バルテルミー",
+      "km": "Saint Barthélemy",
+      "ko": "생바르텔레미",
+      "my": "Saint Barthélemy",
+      "nl": "Saint Barthélemy",
+      "pl": "Saint-Barthélemy",
+      "pt": "São Bartolomeu",
+      "ru": "Сен-Бартелеми",
+      "sk": "Svätý Bartolomej",
+      "th": "เซนต์บาร์เทเลมี",
+      "ur": "سینٹ بارتھیملے",
+      "vi": "Saint Barthélemy",
+      "zh": "圣巴泰勒米"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "290",
+      "247"
+    ],
+    "countryCode": "SH",
+    "countryName": "Saint Helena, Ascension and Tristan da Cunha",
+    "countryNames": {
+      "cs": "Svatá Helena, Ascension a Tristan da Cunha",
+      "cy": "Saint Helena, Ascension and Tristan da Cunha",
+      "de": "St. Helena, Ascension und Tristan da Cunha",
+      "en": "Saint Helena, Ascension and Tristan da Cunha",
+      "es": "Santa Elena, Ascensión y Tristán de Acuña",
+      "et": "Saint Helena, Ascension ja Tristan da Cunha",
+      "fi": "Saint Helena, Ascension ja Tristan da Cunha",
+      "fr": "Sainte-Hélène, Ascension et Tristan da Cunha",
+      "hr": "Sveta Helena",
+      "it": "Sant'Elena, Ascensione e Tristan da Cunha",
+      "ja": "セントヘレナ・アセンションおよびトリスタン ダクーニャ",
+      "km": "Saint Helena, Ascension and Tristan da Cunha",
+      "ko": "세인트헬레나",
+      "my": "Saint Helena, Ascension and Tristan da Cunha",
+      "nl": "Sint-Helena, Ascension en Tristan da Cunha",
+      "pl": "Wyspa Świętej Heleny, Wyspa Wniebowstąpienia i Tristan da Cunha",
+      "pt": "Santa Helena, Ascensão e Tristão da Cunha",
+      "ru": "Острова Святой Елены, Возн есени я и Тристан-да-Кунья",
+      "sk": "Svätá Helena (zámorské územie)",
+      "th": "Saint Helena, Assenson และ Tristan Dakunia",
+      "ur": "سینٹ ہلینا، اسینشن و ترسٹان دا کونیا",
+      "vi": "St Helena, Ascension và Tristan da Cunha",
+      "zh": "圣赫勒拿、阿森松和特里斯坦-达库 尼亚"
+    },
+    "currency": [
+      "SHP",
+      "GBP"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "1869"
+    ],
+    "countryCode": "KN",
+    "countryName": "Saint Kitts and Nevis",
+    "countryNames": {
+      "cs": "Svatý Kryštof a Nevis",
+      "cy": "Saint Kitts and Nevis",
+      "de": "St. Kitts und Nevis",
+      "en": "Saint Kitts and Nevis",
+      "es": "San Cristóbal y Nieves",
+      "et": "Saint Kitts ja Nevis",
+      "fi": "Saint Kitts ja Nevis",
+      "fr": "Saint-Christophe-et-Niévès",
+      "hr": "Sveti Kristof i Nevis",
+      "it": "Saint Kitts e Nevis",
+      "ja": "セントクリストファー・ネイビス",
+      "km": "Saint Kitts and Nevis",
+      "ko": "세인트키츠 네비스",
+      "my": "Saint Kitts and Nevis",
+      "nl": "Saint Kitts en Nevis",
+      "pl": "Saint Kitts i Nevis",
+      "pt": "São Cristóvão e Nevis",
+      "ru": "Сент-Китс и Невис",
+      "sk": "Svätý Krištof a Nevis",
+      "th": "เซนต์คิตส์และเนวิส",
+      "ur": "سینٹ کیٹز و ناویس",
+      "vi": "Saint Kitts and Nevis",
+      "zh": "圣基茨和尼维斯"
+    },
+    "currency": [
+      "XCD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "1758"
+    ],
+    "countryCode": "LC",
+    "countryName": "Saint Lucia",
+    "countryNames": {
+      "cs": "Svatá Lucie",
+      "cy": "Saint Lucia",
+      "de": "St. Lucia",
+      "en": "Saint Lucia",
+      "es": "Santa Lucía",
+      "et": "Saint Lucia",
+      "fi": "Saint Lucia",
+      "fr": "Sainte-Lucie",
+      "hr": "Sveta Lucija",
+      "it": "Santa Lucia",
+      "ja": "セントル シア",
+      "km": "Saint Lucia",
+      "ko": "세인트루시아",
+      "my": "Saint Lucia",
+      "nl": "Saint Lucia",
+      "pl": "Saint Lucia",
+      "pt": "Santa Lúcia",
+      "ru": "Сент-Люсия",
+      "sk": "Svätá Lucia",
+      "th": "เซนต์ลูเซีย",
+      "ur": "سینٹ لوسیا",
+      "vi": "Saint Lucia",
+      "zh": "圣卢 西亚"
+    },
+    "currency": [
+      "XCD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "590"
+    ],
+    "countryCode": "MF",
+    "countryName": "Saint Martin",
+    "countryNames": {
+      "cs": "Svatý Martin (Francie)",
+      "cy": "Saint Martin",
+      "de": "Saint-Martin",
+      "en": "Saint Martin",
+      "es": "Saint Martin",
+      "et": "Saint-Martin",
+      "fi": "Saint-Martin",
+      "fr": "Saint-Martin",
+      "hr": "Sveti Martin",
+      "it": "Saint Martin",
+      "ja": "サン・マルタン(フランス領)",
+      "km": "Saint Martin",
+      "ko": "생마르 탱",
+      "my": "Saint Martin",
+      "nl": "Saint-Martin",
+      "pl": "Saint-Martin",
+      "pt": "São Martinho",
+      "ru": "Сен-Мартен",
+      "sk": "Saint-Martin",
+      "th": "เซนต์มาร์ติน",
+      "ur": "سینٹ مارٹن",
+      "vi": "Saint Martin",
+      "zh": "圣马丁"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "508"
+    ],
+    "countryCode": "PM",
+    "countryName": "Saint Pierre and Miquelon",
+    "countryNames": {
+      "cs": "Saint-Pierre a Miquelon",
+      "cy": "Saint Pierre and Miquelon",
+      "de": "St. Pierre und Miquelon",
+      "en": "Saint Pierre and Miquelon",
+      "es": "San Pedro y Miquelón",
+      "et": "Saint-Pierre ja Miquelon",
+      "fi": "Saint-Pierre ja Miquelon",
+      "fr": "Saint-Pierre-et-Miquelon",
+      "hr": "Sveti Petar i Mikelon",
+      "it": "Saint-Pierre e Miquelon",
+      "ja": "サンピエール島・ミクロン島",
+      "km": "Saint Pierre and Miquelon",
+      "ko": "생피에르 미클 롱",
+      "my": "Saint Pierre and Miquelon",
+      "nl": "Saint Pierre en Miquelon",
+      "pl": "Saint-Pierre i Miquelon",
+      "pt": "Saint-Pierre e Miquelon",
+      "ru": "Сен-Пьер и Микелон",
+      "sk": "Saint Pierre a Miquelon",
+      "th": "เซนต์ปิแอร์และมีเกอลง",
+      "ur": "سینٹ پیئر و میکیلون",
+      "vi": "Thánh Pierre và Miquelon",
+      "zh": "圣皮埃尔和密克隆"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Americas",
+    "subregion": "North America"
+  },
+  {
+    "callingCode": [
+      "1784"
+    ],
+    "countryCode": "VC",
+    "countryName": "Saint Vincent and the Grenadines",
+    "countryNames": {
+      "cs": "Svatý Vincenc a Grenadiny",
+      "cy": "Saint Vincent and the Grenadines",
+      "de": "St. Vincent und die Grenadinen",
+      "en": "Saint Vincent and the Grenadines",
+      "es": "San Vicente y Granadinas",
+      "et": "Saint Vincent",
+      "fi": "Saint Vincent ja Grenadiinit",
+      "fr": "Saint-Vincent-et-les-Grenadines",
+      "hr": "Sveti Vincent i Grenadini",
+      "it": "Saint Vincent e Grenadine",
+      "ja": "セントビンセントおよびグ レナディーン諸島",
+      "km": "Saint Vincent and the Grenadines",
+      "ko": "세인트빈센트 그레나딘",
+      "my": "Saint Vincent and the Grenadines",
+      "nl": "Saint Vincent en de Grenadines",
+      "pl": "Saint Vincent i Grenadyny",
+      "pt": "São Vincente e Granadinas",
+      "ru": "Сент-Винсент и Гренадины",
+      "sk": "Svätý Vincent a Grenadíny",
+      "th": "เซนต์วินเซนต์และเกรนาดีนส์",
+      "ur": "سینٹ وینسینٹ و گریناڈائنز",
+      "vi": "Saint Vincent và Grenadines",
+      "zh": "圣文森特和格林纳丁斯"
+    },
+    "currency": [
+      "XCD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "685"
+    ],
+    "countryCode": "WS",
+    "countryName": "Samoa",
+    "countryNames": {
+      "cs": "Samoa",
+      "cy": "Samoa",
+      "de": "Samoa",
+      "en": "Samoa",
+      "es": "Samoa",
+      "et": "Samoa",
+      "fi": "Samoa",
+      "fr": "Samoa",
+      "hr": "Samoa",
+      "it": "Samoa",
+      "ja": "サモア",
+      "km": "Samoa",
+      "ko": "사모아",
+      "my": "Samoa",
+      "nl": "Samoa",
+      "pl": "Samoa",
+      "pt": "Samoa",
+      "ru": "Самоа",
+      "sk": "Samoa",
+      "th": "ซามัว",
+      "ur": "سامووا",
+      "vi": "Samoa",
+      "zh": "萨摩亚"
+    },
+    "currency": [
+      "WST"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "378"
+    ],
+    "countryCode": "SM",
+    "countryName": "San Marino",
+    "countryNames": {
+      "cs": "San Marino",
+      "cy": "San Marino",
+      "de": "San Marino",
+      "en": "San Marino",
+      "es": "San Marino",
+      "et": "San Marino",
+      "fi": "San Marino",
+      "fr": "Saint-Marin",
+      "hr": "San Marino",
+      "it": "San Marino",
+      "ja": "サ ンマリノ",
+      "km": "San Marino",
+      "ko": "산마리노",
+      "my": "San Marino",
+      "nl": "San Marino",
+      "pl": "San Marino",
+      "pt": "San Marino",
+      "ru": " Сан-Марино",
+      "sk": "San Maríno",
+      "th": "ซานมารีโน",
+      "ur": "سان مارینو",
+      "vi": "San Marino",
+      "zh": "圣马力诺"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "966"
+    ],
+    "countryCode": "SA",
+    "countryName": "Saudi Arabia",
+    "countryNames": {
+      "cs": "Saúdská Arábie",
+      "cy": "Saudi Arabia",
+      "de": "Saudi-Arabien",
+      "en": "Saudi Arabia",
+      "es": "Arabia Saudí",
+      "et": "Saudi Araabia",
+      "fi": "Saudi-Arabia",
+      "fr": "Arabie Saoudite",
+      "hr": "Saudijska Arabija",
+      "it": "Arabia Saudita",
+      "ja": "サウジアラビア",
+      "km": "Saudi Arabia",
+      "ko": "사우디아라비아",
+      "my": "Saudi Arabia",
+      "nl": "Saoedi-Arabië",
+      "pl": "Arabia Saudyjska",
+      "pt": "Arábia Saudita",
+      "ru": "Сауд овская Аравия",
+      "sk": "Saudská Arábia",
+      "th": "ซาอุดีอาระเบีย",
+      "ur": "سعودی عرب",
+      "vi": "Ả Rập Saudi",
+      "zh": "沙特阿拉伯"
+    },
+    "currency": [
+      "SAR"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "221"
+    ],
+    "countryCode": "SN",
+    "countryName": "Senegal",
+    "countryNames": {
+      "cs": "Senegal",
+      "cy": "Senegal",
+      "de": "Senegal",
+      "en": "Senegal",
+      "es": "Senegal",
+      "et": "Senegal",
+      "fi": "Senegal",
+      "fr": "Sénégal",
+      "hr": "Senegal",
+      "it": "Senegal",
+      "ja": "セネガル",
+      "km": "Senegal",
+      "ko": "세네갈",
+      "my": "Senegal",
+      "nl": "Senegal",
+      "pl": "Senegal",
+      "pt": "Senegal",
+      "ru": "Сене гал",
+      "sk": "Senegal",
+      "th": "เซเนกัล",
+      "ur": "سینیگال",
+      "vi": "Senegal",
+      "zh": "塞内加尔"
+    },
+    "currency": [
+      "XOF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "381"
+    ],
+    "countryCode": "RS",
+    "countryName": "Serbia",
+    "countryNames": {
+      "cs": "Srbsko",
+      "cy": "Serbia",
+      "de": "Serbien",
+      "en": "Serbia",
+      "es": "Serbia",
+      "et": "Serbia",
+      "fi": "Serbia",
+      "fr": "Serbie",
+      "hr": "Srbija",
+      "it": "Serbia",
+      "ja": " セルビア",
+      "km": "Serbia",
+      "ko": "세르비아",
+      "my": "Serbia",
+      "nl": "Servië",
+      "pl": "Serbia",
+      "pt": "Sérvia",
+      "ru": "Сербия",
+      "sk": "Srbsko",
+      "th": "เซอร์เบีย",
+      "ur": "سربیا",
+      "vi": "Serbia",
+      "zh": "塞尔维亚"
+    },
+    "currency": [
+      "RSD"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "248"
+    ],
+    "countryCode": "SC",
+    "countryName": "Seychelles",
+    "countryNames": {
+      "cs": "Seychely",
+      "cy": "Seychelles",
+      "de": "Seychellen",
+      "en": "Seychelles",
+      "es": "Seychelles",
+      "et": "Seišellid",
+      "fi": "Seychellit",
+      "fr": "Seychelles",
+      "hr": "Sejšeli",
+      "it": "Seychelles",
+      "ja": "セーシェル",
+      "km": "Seychelles",
+      "ko": "세이셸",
+      "my": "Seychelles",
+      "nl": "Seychellen",
+      "pl": "Seszele",
+      "pt": "Seicheles",
+      "ru": "Сейшельские Острова",
+      "sk": "Seychely",
+      "th": "เซเชลส์",
+      "ur": "سیچیلیس",
+      "vi": "Seychelles",
+      "zh": "塞舌尔"
+    },
+    "currency": [
+      "SCR"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "232"
+    ],
+    "countryCode": "SL",
+    "countryName": "Sierra Leone",
+    "countryNames": {
+      "cs": "Sierra Leone",
+      "cy": "Sierra Leone",
+      "de": "Sierra Leone",
+      "en": "Sierra Leone",
+      "es": "Sierra Leone",
+      "et": "Sierra Leone",
+      "fi": "Sierra Leone",
+      "fr": "Sierra Leone",
+      "hr": "Sijera Leone",
+      "it": "Sierra Leone",
+      "ja": "シエラレオネ",
+      "km": "Sierra Leone",
+      "ko": "시에라리온",
+      "my": "Sierra Leone",
+      "nl": "Sierra Leone",
+      "pl": "Sierra Leone",
+      "pt": "Serra Leoa",
+      "ru": "Сьерра-Леоне",
+      "sk": "Sierra Leone",
+      "th": "เซียร์ราลีโอน",
+      "ur": "سیرالیون",
+      "vi": "Sierra Leone",
+      "zh": "塞拉利昂"
+    },
+    "currency": [
+      "SLL"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "65"
+    ],
+    "countryCode": "SG",
+    "countryName": "Singapore",
+    "countryNames": {
+      "cs": "Singapur",
+      "cy": "Singapore",
+      "de": "Singapur",
+      "en": "Singapore",
+      "es": "Singapur",
+      "et": "Singapur",
+      "fi": "Singapore",
+      "fr": "Singapour",
+      "hr": "Singapur",
+      "it": "Singapore",
+      "ja": "シンガポー ル",
+      "km": "Singapore",
+      "ko": "싱가포르",
+      "my": "Singapore",
+      "nl": "Singapore",
+      "pl": "Singapur",
+      "pt": "Singapura",
+      "ru": "Сингапур",
+      "sk": "Singapur",
+      "th": "สิงคโปร์",
+      "ur": "سنگاپور",
+      "vi": "Singapore",
+      "zh": "新加坡"
+    },
+    "currency": [
+      "SGD"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "1721"
+    ],
+    "countryCode": "SX",
+    "countryName": "Sint Maarten",
+    "countryNames": {
+      "cs": "Svatý Martin (Nizozemsko)",
+      "cy": "Sint Maarten",
+      "de": "Sint Maarten",
+      "en": "Sint Maarten",
+      "es": "Sint Maarten",
+      "et": "Sint Maarten",
+      "fi": "Sint Maarten",
+      "fr": "Saint-Martin",
+      "hr": "Sveti Martin",
+      "it": "Sint Maarten",
+      "ja": "シント・ マールテン",
+      "km": "Sint Maarten",
+      "ko": " 신트마르턴",
+      "my": "Sint Maarten",
+      "nl": "Sint Maarten",
+      "pl": "Sint Maarten",
+      "pt": "São Martinho",
+      "ru": "Синт-Мартен",
+      "sk": "Sint Maarten",
+      "th": "เซนต์มาร์ติน",
+      "ur": "سنٹ مارٹن",
+      "vi": "Đảo Saint Martin",
+      "zh": "圣马丁岛"
+    },
+    "currency": [
+      "ANG"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "421"
+    ],
+    "countryCode": "SK",
+    "countryName": "Slovakia",
+    "countryNames": {
+      "cs": "Slovensko",
+      "cy": "Slovakia",
+      "de": "Slowakei",
+      "en": "Slovakia",
+      "es": "República Eslovaca",
+      "et": "Slovakkia",
+      "fi": "Slovakia",
+      "fr": "Slovaquie",
+      "hr": "Slovačka",
+      "it": "Slovacchia",
+      "ja": "スロバキア",
+      "km": "Slovakia",
+      "ko": "슬로바키아",
+      "my": "Slovakia",
+      "nl": "Slowakije",
+      "pl": "Słowacja",
+      "pt": "Eslováquia",
+      "ru": "Словакия",
+      "sk": "Slovensko",
+      "th": "สโลวาเกีย",
+      "ur": "سلوواکیہ",
+      "vi": "Slovakia",
+      "zh": " 斯洛伐克"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Central Europe"
+  },
+  {
+    "callingCode": [
+      "386"
+    ],
+    "countryCode": "SI",
+    "countryName": "Slovenia",
+    "countryNames": {
+      "cs": "Slovinsko",
+      "cy": "Slovenia",
+      "de": "Slowenien",
+      "en": "Slovenia",
+      "es": "Eslovenia",
+      "et": "Sloveenia",
+      "fi": "Slovenia",
+      "fr": "Slovénie",
+      "hr": "Slovenija",
+      "it": "Slovenia",
+      "ja": "スロベニア",
+      "km": "Slovenia",
+      "ko": "슬로베니아",
+      "my": "Slovenia",
+      "nl": "Slovenië",
+      "pl": "Słowenia",
+      "pt": "Eslovénia",
+      "ru": "Словения",
+      "sk": "Slovinsko",
+      "th": "สโลวีเนีย",
+      "ur": "سلووینیا",
+      "vi": "Slovenia",
+      "zh": "斯洛文尼亚"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "677"
+    ],
+    "countryCode": "SB",
+    "countryName": "Solomon Islands",
+    "countryNames": {
+      "cs": "Šalamounovy ostrovy",
+      "cy": "Solomon Islands",
+      "de": "Salomonen",
+      "en": "Solomon Islands",
+      "es": "Islas Salomón",
+      "et": "Saalomoni Saared",
+      "fi": "Salomonsaaret",
+      "fr": "Îles Salomon",
+      "hr": "Solomonski Otoci",
+      "it": "Isole Salomone",
+      "ja": "ソロモン諸島",
+      "km": "Solomon Islands",
+      "ko": " 솔로몬 제도",
+      "my": "Solomon Islands",
+      "nl": "Salomonseilanden",
+      "pl": "Wyspy Salomona",
+      "pt": "Ilhas Salomão",
+      "ru": " Соломоновы Ост рова",
+      "sk": "Salomonove ostrovy",
+      "th": "หมู่เกาะโซโลมอน",
+      "ur": "جزائر سلیمان",
+      "vi": "Quần đảo Solomon",
+      "zh": "所罗门群 岛"
+    },
+    "currency": [
+      "SBD"
+    ],
+    "region": "Oceania",
+    "subregion": "Melanesia"
+  },
+  {
+    "callingCode": [
+      "252"
+    ],
+    "countryCode": "SO",
+    "countryName": "Somalia",
+    "countryNames": {
+      "cs": "Somálsko",
+      "cy": "Somalia",
+      "de": "Somalia",
+      "en": "Somalia",
+      "es": "Somalia",
+      "et": "Somaalia",
+      "fi": "Somalia",
+      "fr": "Somalie",
+      "hr": "Somalija",
+      "it": "Somalia",
+      "ja": "ソマリア",
+      "km": "Somalia",
+      "ko": "소말리아",
+      "my": "Somalia",
+      "nl": "Somalië",
+      "pl": "Somalia",
+      "pt": "Somália",
+      "ru": "Сомали",
+      "sk": "Somálsko",
+      "th": "โซมาเลีย",
+      "ur": "صومالیہ",
+      "vi": "Somalia",
+      "zh": "索马 里"
+    },
+    "currency": [
+      "SOS"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "27"
+    ],
+    "countryCode": "ZA",
+    "countryName": "South Africa",
+    "countryNames": {
+      "cs": "Jihoafrická republika",
+      "cy": "South Africa",
+      "de": "Südafrika",
+      "en": "South Africa",
+      "es": "República de Sudáfrica",
+      "et": "Lõuna-Aafrika Vabariik",
+      "fi": "Etelä-Afrikka",
+      "fr": "Afrique du Sud",
+      "hr": "Južnoafrička Republika",
+      "it": "Sud Africa",
+      "ja": "南アフリカ",
+      "km": "South Africa",
+      "ko": "남아프리카",
+      "my": "South Africa",
+      "nl": "Zuid-Afrika",
+      "pl": "Południowa Afryka",
+      "pt": "África do Sul",
+      "ru": "Южно-Африканская Республика",
+      "sk": "Juhoafrická republika",
+      "th": "แอฟริกาใต้",
+      "ur": "جنوبی افریقا",
+      "vi": "Nam Phi",
+      "zh": "南非"
+    },
+    "currency": [
+      "ZAR"
+    ],
+    "region": "Africa",
+    "subregion": "Southern Africa"
+  },
+  {
+    "callingCode": [
+      "500"
+    ],
+    "countryCode": "GS",
+    "countryName": "South Georgia",
+    "countryNames": {
+      "cs": "Jižní Georgie a Jižní Sandwichovy ostrovy",
+      "cy": "South Georgia",
+      "de": "Südgeorgien und die Südlichen Sandwichinseln",
+      "en": "South Georgia",
+      "es": "Islas Georgias del Sur y Sandwich del Sur",
+      "et": "Lõuna-Georgia ja Lõuna-Sandwichi saared",
+      "fi": "Etelä-Georgia ja Eteläiset Sandwichsaaret",
+      "fr": "Géorgie du Sud-et-les Îles Sandwich du Sud",
+      "hr": "Južna Georgija i otočje Južni Sandwich",
+      "it": "Georgia del Sud e Isole Sandwich Meridionali",
+      "ja": "サウスジョージア・サウスサンドウィッチ諸島",
+      "km": "South Georgia",
+      "ko": "조지아",
+      "my": "South Georgia",
+      "nl": "Zuid-Georgia en Zuidelijke Sandwicheilanden",
+      "pl": "Georgia Południowa i Sandwich Południowy",
+      "pt": "Ilhas Geórgia do Sul e Sandwich do Sul",
+      "ru": "Южная Георгия и Юж ные Сандвичевы острова",
+      "sk": "Južná Georgia a Južné Sandwichove ostrovy",
+      "th": "เซาท์จอร์เจีย",
+      "ur": "جنوبی جارجیا",
+      "vi": "Nam Georgia",
+      "zh": "南乔治亚"
+    },
+    "currency": [
+      "GBP"
+    ],
+    "region": "Antarctic",
+    "subregion": ""
+  },
+  {
+    "callingCode": [
+      "82"
+    ],
+    "countryCode": "KR",
+    "countryName": "South Korea",
+    "countryNames": {
+      "cs": "Jižní Korea",
+      "cy": "South Korea",
+      "de": "Südkorea",
+      "en": "South Korea",
+      "es": "Corea del Sur",
+      "et": "Lõuna-Korea",
+      "fi": "Etelä-Korea",
+      "fr": "Corée du Sud",
+      "hr": "Južna Koreja",
+      "it": "Corea del Sud",
+      "ja": "韓国",
+      "km": "South Korea",
+      "ko": "한국",
+      "my": "South Korea",
+      "nl": "Zuid-Korea",
+      "pl": "Korea Południowa",
+      "pt": "Coreia do Sul",
+      "ru": "Южная Ко рея",
+      "sk": "Južná Kórea",
+      "th": "เกาหลี",
+      "ur": "جنوبی کوریا",
+      "vi": "Hàn Quốc",
+      "zh": "韩国"
+    },
+    "currency": [
+      "KRW"
+    ],
+    "region": "Asia",
+    "subregion": "Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "211"
+    ],
+    "countryCode": "SS",
+    "countryName": "South Sudan",
+    "countryNames": {
+      "cs": "Jižní Súdán",
+      "cy": "South Sudan",
+      "de": "Südsudan",
+      "en": "South Sudan",
+      "es": "Sudán del Sur",
+      "et": "Lõuna-Sudaan",
+      "fi": "Etelä-Sudan",
+      "fr": "Soudan du Sud",
+      "hr": "Južni Sudan",
+      "it": "Sudan del sud",
+      "ja": " 南スーダン",
+      "km": "South Sudan",
+      "ko": "남수단",
+      "my": "South Sudan",
+      "nl": "Zuid-Soedan",
+      "pl": "Sudan",
+      "pt": "Sudão do Sul",
+      "ru": " Южный Судан",
+      "sk": "Južný Sudán",
+      "th": "เซาท์ซูดาน",
+      "ur": "جنوبی سوڈان",
+      "vi": "Nam Sudan",
+      "zh": "南苏丹"
+    },
+    "currency": [
+      "SSP"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "34"
+    ],
+    "countryCode": "ES",
+    "countryName": "Spain",
+    "countryNames": {
+      "cs": "Španělsko",
+      "cy": "Spain",
+      "de": "Spanien",
+      "en": "Spain",
+      "es": "España",
+      "et": "Hispaania",
+      "fi": "Espanja",
+      "fr": "Espagne",
+      "hr": "Španjolska",
+      "it": "Spagna",
+      "ja": "ス ペイン",
+      "km": "Spain",
+      "ko": "스페 인",
+      "my": "Spain",
+      "nl": "Spanje",
+      "pl": "Hiszpania",
+      "pt": "Espanha",
+      "ru": "Ис пания",
+      "sk": "Španielsko",
+      "th": "สเปน",
+      "ur": "ہسپانیہ",
+      "vi": "Tây Ban Nha",
+      "zh": "西班牙"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "94"
+    ],
+    "countryCode": "LK",
+    "countryName": "Sri Lanka",
+    "countryNames": {
+      "cs": "Srí Lanka",
+      "cy": "Sri Lanka",
+      "de": "Sri Lanka",
+      "en": "Sri Lanka",
+      "es": "Sri Lanka",
+      "et": "Sri Lanka",
+      "fi": "Sri Lanka",
+      "fr": "Sri Lanka",
+      "hr": "Šri Lanka",
+      "it": "Sri Lanka",
+      "ja": "スリランカ",
+      "km": "Sri Lanka",
+      "ko": "스리랑카",
+      "my": "Sri Lanka",
+      "nl": "Sri Lanka",
+      "pl": "Sri Lanka",
+      "pt": "Sri Lanka",
+      "ru": "Шри-Ла нка",
+      "sk": "Srí Lanka",
+      "th": "ศรีลังกา",
+      "ur": "سری لنکا",
+      "vi": "Sri Lanka",
+      "zh": "斯里兰卡"
+    },
+    "currency": [
+      "LKR"
+    ],
+    "region": "Asia",
+    "subregion": "Southern Asia"
+  },
+  {
+    "callingCode": [
+      "249"
+    ],
+    "countryCode": "SD",
+    "countryName": "Sudan",
+    "countryNames": {
+      "cs": "Súdán",
+      "cy": "Sudan",
+      "de": "Sudan",
+      "en": "Sudan",
+      "es": "Sudán",
+      "et": "Sudaan",
+      "fi": "Sudan",
+      "fr": "Soudan",
+      "hr": "Sudan",
+      "it": "Sudan",
+      "ja": "スーダン",
+      "km": "Sudan",
+      "ko": "수단",
+      "my": "Sudan",
+      "nl": "Soedan",
+      "pl": "Sudan",
+      "pt": "Sudão",
+      "ru": "Судан",
+      "sk": "Sudán",
+      "th": "ซูดาน",
+      "ur": "سودان",
+      "vi": "Sudan",
+      "zh": "苏丹"
+    },
+    "currency": [
+      "SDG"
+    ],
+    "region": "Africa",
+    "subregion": "Northern Africa"
+  },
+  {
+    "callingCode": [
+      "597"
+    ],
+    "countryCode": "SR",
+    "countryName": "Suriname",
+    "countryNames": {
+      "cs": "Surinam",
+      "cy": "Suriname",
+      "de": "Suriname",
+      "en": "Suriname",
+      "es": "Surinam",
+      "et": "Suriname",
+      "fi": "Suriname",
+      "fr": "Surinam",
+      "hr": "Surinam",
+      "it": "Suriname",
+      "ja": "スリナム",
+      "km": "Suriname",
+      "ko": "수리남",
+      "my": "Suriname",
+      "nl": "Suriname",
+      "pl": "Surinam",
+      "pt": "Suriname",
+      "ru": "Суринам",
+      "sk": "Surinam",
+      "th": "ซูรินาเม",
+      "ur": "سرینام",
+      "vi": "Suriname",
+      "zh": "苏里南"
+    },
+    "currency": [
+      "SRD"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "4779"
+    ],
+    "countryCode": "SJ",
+    "countryName": "Svalbard and Jan Mayen",
+    "countryNames": {
+      "cs": "Špicberky a Jan Mayen",
+      "cy": "Svalbard and Jan Mayen",
+      "de": "Spitzbergen und Jan Mayen",
+      "en": "Svalbard and Jan Mayen",
+      "es": "Islas Svalbard y Jan Mayen",
+      "et": "Svalbard",
+      "fi": "Huippuvuoret",
+      "fr": "Svalbard et Jan Mayen",
+      "hr": "Svalbard i Jan Mayen",
+      "it": "Svalbard e Jan Mayen",
+      "ja": "スヴァー  ルバル諸島およびヤンマイエン島",
+      "km": "Svalbard and Jan Mayen",
+      "ko": "스발바르 얀마옌 제도",
+      "my": "Svalbard and Jan Mayen",
+      "nl": "Svalbard en Jan Mayen",
+      "pl": "Svalbard i Jan Mayen",
+      "pt": "Ilhas Svalbard e Jan Mayen",
+      "ru": "Шпиц берген и Ян-Майен",
+      "sk": "Svalbard a Jan Mayen",
+      "th": "Svalbart",
+      "ur": "سوالبارڈ اور جان میئن",
+      "vi": "Svalbard and Jan Mayen",
+      "zh": "斯瓦尔巴特"
+    },
+    "currency": [
+      "NOK"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "46"
+    ],
+    "countryCode": "SE",
+    "countryName": "Sweden",
+    "countryNames": {
+      "cs": "Švédsko",
+      "cy": "Sweden",
+      "de": "Schweden",
+      "en": "Sweden",
+      "es": "Suecia",
+      "et": "Rootsi",
+      "fi": "Ruotsi",
+      "fr": "Suède",
+      "hr": "Švedska",
+      "it": "Svezia",
+      "ja": "スウェーデン",
+      "km": "Sweden",
+      "ko": "스웨덴",
+      "my": "Sweden",
+      "nl": "Zweden",
+      "pl": "Szwecja",
+      "pt": "Suécia",
+      "ru": "Швеция",
+      "sk": "Švédsko",
+      "th": "สวีเดน",
+      "ur": "سویڈن",
+      "vi": "Thụy Điển",
+      "zh": "瑞典"
+    },
+    "currency": [
+      "SEK"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "41"
+    ],
+    "countryCode": "CH",
+    "countryName": "Switzerland",
+    "countryNames": {
+      "cs": "Švýcarsko",
+      "cy": "Switzerland",
+      "de": "Schweiz",
+      "en": "Switzerland",
+      "es": "Suiza",
+      "et": "Šveits",
+      "fi": "Sveitsi",
+      "fr": "Suisse",
+      "hr": "Švicarska",
+      "it": "Svizzera",
+      "ja": "スイス",
+      "km": "Switzerland",
+      "ko": "스위스",
+      "my": "Switzerland",
+      "nl": "Zwitserland",
+      "pl": "Szwajcaria",
+      "pt": "Suíça",
+      "ru": "Шв е йцари я",
+      "sk": "Švajčiarsko",
+      "th": "สวิตเซอร์แลนด์",
+      "ur": "سویٹذرلینڈ",
+      "vi": "Thụy Sĩ",
+      "zh": "瑞士"
+    },
+    "currency": [
+      "CHF"
+    ],
+    "region": "Europe",
+    "subregion": "Western Europe"
+  },
+  {
+    "callingCode": [
+      "963"
+    ],
+    "countryCode": "SY",
+    "countryName": "Syria",
+    "countryNames": {
+      "cs": "Sýrie",
+      "cy": "Syria",
+      "de": "Syrien",
+      "en": "Syria",
+      "es": "Siria",
+      "et": "Süüria",
+      "fi": "Syyria",
+      "fr": "Syrie",
+      "hr": "Sirija",
+      "it": "Siria",
+      "ja": "シリア・アラブ共和国",
+      "km": "Syria",
+      "ko": "시리아",
+      "my": "Syria",
+      "nl": "Syrië",
+      "pl": "Syria",
+      "pt": "Síria",
+      "ru": "Сирия",
+      "sk": "Sýria",
+      "th": "ซีเรีย",
+      "ur": "سوریہ",
+      "vi": "Syria",
+      "zh": "叙利亚"
+    },
+    "currency": [
+      "SYP"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "239"
+    ],
+    "countryCode": "ST",
+    "countryName": "São Tomé and Príncipe",
+    "countryNames": {
+      "cs": "Svatý Tomáš a Princův ostrov",
+      "cy": "São Tomé and Príncipe",
+      "de": "São Tomé und Príncipe",
+      "en": "São Tomé and Príncipe",
+      "es": "Santo Tomé y Príncipe",
+      "et": "São Tomé ja Príncipe",
+      "fi": "São Téme ja Príncipe",
+      "fr": "São Tomé et Príncipe",
+      "hr": "Sveti Toma i Princip",
+      "it": "São Tomé e Príncipe",
+      "ja": "サントメ・プリンシペ",
+      "km": "São Tomé and Príncipe",
+      "ko": "상투메 프린시페",
+      "my": "São Tomé and Príncipe",
+      "nl": "Sao Tomé en Principe",
+      "pl": "Wyspy Świętego Tomasza i Książęca",
+      "pt": "São Tomé e Príncipe",
+      "ru": "Сан-Томе и Принс ипи",
+      "sk": "Svätý Tomáš a Princov ostrov",
+      "th": "เซาตูเมและปรินซิปี",
+      "ur": "ساؤ ٹومے و پرنسپے",
+      "vi": "São Tomé And Príncipe",
+      "zh": "圣多美和普林西比"
+    },
+    "currency": [
+      "STD"
+    ],
+    "region": "Africa",
+    "subregion": "Middle Africa"
+  },
+  {
+    "callingCode": [
+      "886"
+    ],
+    "countryCode": "TW",
+    "countryName": "Taiwan",
+    "countryNames": {
+      "cs": "Tchaj-wan",
+      "cy": "Taiwan",
+      "de": "Taiwan",
+      "en": "Taiwan",
+      "es": "Taiwán",
+      "et": "Taiwan",
+      "fi": "Taiwan",
+      "fr": "Taïwan",
+      "hr": "Tajvan",
+      "it": "Taiwan",
+      "ja": "台湾",
+      "km": "Taiwan",
+      "ko": "대만",
+      "my": "Taiwan",
+      "nl": "Taiwan",
+      "pl": "Tajwan",
+      "pt": "Ilha Formosa",
+      "ru": "Тайвань",
+      "sk": "Taiwan",
+      "th": "ไต้หวัน",
+      "ur": "تائیوان",
+      "vi": "Đài Loan",
+      "zh": "台湾"
+    },
+    "currency": [
+      "TWD"
+    ],
+    "region": "Asia",
+    "subregion": "Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "992"
+    ],
+    "countryCode": "TJ",
+    "countryName": "Tajikistan",
+    "countryNames": {
+      "cs": "Tádžikistán",
+      "cy": "Tajikistan",
+      "de": "Tadschikistan",
+      "en": "Tajikistan",
+      "es": "Tayikistán",
+      "et": "Tadžikistan",
+      "fi": "Tadžikistan",
+      "fr": "Tadjikistan",
+      "hr": "Tađikistan",
+      "it": "Tagikistan",
+      "ja": "タジキスタン",
+      "km": "Tajikistan",
+      "ko": "타지키스탄",
+      "my": "Tajikistan",
+      "nl": "Tadzjikistan",
+      "pl": "Tadżykistan",
+      "pt": "Tajiquistão",
+      "ru": "Таджикистан",
+      "sk": "Tadžikistan",
+      "th": "ทาจิกิสถาน",
+      "ur": "تاجکستان",
+      "vi": "Tajikistan",
+      "zh": "塔吉克斯坦"
+    },
+    "currency": [
+      "TJS"
+    ],
+    "region": "Asia",
+    "subregion": "Central Asia"
+  },
+  {
+    "callingCode": [
+      "255"
+    ],
+    "countryCode": "TZ",
+    "countryName": "Tanzania",
+    "countryNames": {
+      "cs": "Tanzanie",
+      "cy": "Tanzania",
+      "de": "Tansania",
+      "en": "Tanzania",
+      "es": "Tanzania",
+      "et": "Tansaania",
+      "fi": "Tansania",
+      "fr": "Tanzanie",
+      "hr": "Tanzanija",
+      "it": "Tanzania",
+      "ja": "タンザニア",
+      "km": "Tanzania",
+      "ko": "탄자니아",
+      "my": "Tanzania",
+      "nl": "Tanzania",
+      "pl": "Tanzania",
+      "pt": "Tanzânia",
+      "ru": "Танзания",
+      "sk": "Tanzánia",
+      "th": "แทนซาเนีย",
+      "ur": "تنزانیہ",
+      "vi": "Tanzania",
+      "zh": "坦桑尼亚"
+    },
+    "currency": [
+      "TZS"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "66"
+    ],
+    "countryCode": "TH",
+    "countryName": "Thailand",
+    "countryNames": {
+      "cs": "Thajsko",
+      "cy": "Thailand",
+      "de": "Thailand",
+      "en": "Thailand",
+      "es": "Tailandia",
+      "et": "Tai",
+      "fi": "Thaimaa",
+      "fr": "Thaïlande",
+      "hr": "Tajland",
+      "it": "Tailandia",
+      "ja": "タイ",
+      "km": "Thailand",
+      "ko": "태국",
+      "my": "Thailand",
+      "nl": "Thailand",
+      "pl": "Tajlandia",
+      "pt": "Tailândia",
+      "ru": "Таиланд",
+      "sk": "Thajsko",
+      "th": "ประเทศไทย",
+      "ur": "تھائی لینڈ",
+      "vi": "Thái Lan",
+      "zh": "泰国"
+    },
+    "currency": [
+      "THB"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "670"
+    ],
+    "countryCode": "TL",
+    "countryName": "Timor-Leste",
+    "countryNames": {
+      "cs": "Východní Timor",
+      "cy": "Timor-Leste",
+      "de": "Osttimor",
+      "en": "Timor-Leste",
+      "es": "Timor Oriental",
+      "et": "Ida-Timor",
+      "fi": "Itä-Timor",
+      "fr": "Timor oriental",
+      "hr": "Istočni Timor",
+      "it": "Timor Est",
+      "ja": "東ティモール",
+      "km": "Timor-Leste",
+      "ko": "동티모르",
+      "my": "Timor-Leste",
+      "nl": "Oost-Timor",
+      "pl": "Timor Wschodni",
+      "pt": "Timor-Leste",
+      "ru": "Восточ ный Тимор",
+      "sk": "Východný Timor",
+      "th": "ติมอร์ตะวันออก",
+      "ur": "مشرقی تیمور",
+      "vi": "Đông Timor",
+      "zh": "东帝汶"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "228"
+    ],
+    "countryCode": "TG",
+    "countryName": "Togo",
+    "countryNames": {
+      "cs": "Togo",
+      "cy": "Togo",
+      "de": "Togo",
+      "en": "Togo",
+      "es": "Togo",
+      "et": "Togo",
+      "fi": "Togo",
+      "fr": "Togo",
+      "hr": "Togo",
+      "it": "Togo",
+      "ja": "トーゴ",
+      "km": "Togo",
+      "ko": "토고",
+      "my": "Togo",
+      "nl": "Togo",
+      "pl": "Togo",
+      "pt": "Togo",
+      "ru": "Того",
+      "sk": "Togo",
+      "th": "โตโก",
+      "ur": "ٹوگو",
+      "vi": "Togo",
+      "zh": "多哥"
+    },
+    "currency": [
+      "XOF"
+    ],
+    "region": "Africa",
+    "subregion": "Western Africa"
+  },
+  {
+    "callingCode": [
+      "690"
+    ],
+    "countryCode": "TK",
+    "countryName": "Tokelau",
+    "countryNames": {
+      "cs": "Tokelau",
+      "cy": "Tokelau",
+      "de": "Tokelau",
+      "en": "Tokelau",
+      "es": "Islas Tokelau",
+      "et": "Tokelau",
+      "fi": "Tokelau",
+      "fr": "Tokelau",
+      "hr": "Tokelau",
+      "it": "Isole Tokelau",
+      "ja": "トケラウ",
+      "km": "Tokelau",
+      "ko": "토켈라우",
+      "my": "Tokelau",
+      "nl": "Tokelau",
+      "pl": "Tokelau",
+      "pt": "Tokelau",
+      "ru": "Токелау",
+      "sk": "Tokelau",
+      "th": "โตเกเลา",
+      "ur": "ٹوکیلاؤ",
+      "vi": "Tokelau",
+      "zh": "托克劳"
+    },
+    "currency": [
+      "NZD"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "676"
+    ],
+    "countryCode": "TO",
+    "countryName": "Tonga",
+    "countryNames": {
+      "cs": "Tonga",
+      "cy": "Tonga",
+      "de": "Tonga",
+      "en": "Tonga",
+      "es": "Tonga",
+      "et": "Tonga",
+      "fi": "Tonga",
+      "fr": "Tonga",
+      "hr": "Tonga",
+      "it": "Tonga",
+      "ja": "トンガ",
+      "km": "Tonga",
+      "ko": "통가",
+      "my": "Tonga",
+      "nl": "Tonga",
+      "pl": "Tonga",
+      "pt": "Tonga",
+      "ru": "Тонга",
+      "sk": "Tonga",
+      "th": "Tonga",
+      "ur": "ٹونگا",
+      "vi": "Tonga",
+      "zh": "汤加"
+    },
+    "currency": [
+      "TOP"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "1868"
+    ],
+    "countryCode": "TT",
+    "countryName": "Trinidad and Tobago",
+    "countryNames": {
+      "cs": "Trinidad a Tobago",
+      "cy": "Trinidad and Tobago",
+      "de": "Trinidad und Tobago",
+      "en": "Trinidad and Tobago",
+      "es": "Trinidad y Tobago",
+      "et": "Trinidad ja Tobago",
+      "fi": "Trinidad ja Tobago",
+      "fr": "Trinité-et-Tobago",
+      "hr": "Trinidad i Tobago",
+      "it": "Trinidad e Tobago",
+      "ja": "トリニダード・トバゴ",
+      "km": "Trinidad and Tobago",
+      "ko": "트리니다드 토바고",
+      "my": "Trinidad and Tobago",
+      "nl": "Trinidad en Tobago",
+      "pl": "Trynidad i Tobago",
+      "pt": "Trinidade e Tobago",
+      "ru": "Тринидад и Тобаго",
+      "sk": "Trinidad a Tobago",
+      "th": "ตรินิแดดและโตเบโก",
+      "ur": "ٹرینیڈاڈ و ٹوباگو",
+      "vi": "Trinidad và Tobago",
+      "zh": "特立尼达和多巴哥"
+    },
+    "currency": [
+      "TTD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "216"
+    ],
+    "countryCode": "TN",
+    "countryName": "Tunisia",
+    "countryNames": {
+      "cs": "Tunisko",
+      "cy": "Tunisia",
+      "de": "Tunesien",
+      "en": "Tunisia",
+      "es": "Túnez",
+      "et": "Tuneesia",
+      "fi": "Tunisia",
+      "fr": "Tunisie",
+      "hr": "Tunis",
+      "it": "Tunisia",
+      "ja": "チュニジア",
+      "km": "Tunisia",
+      "ko": "튀니지",
+      "my": "Tunisia",
+      "nl": "Tunesië",
+      "pl": "Tunezja",
+      "pt": "Tunísia",
+      "ru": "Тунис",
+      "sk": "Tunisko",
+      "th": "ตูนิเซีย",
+      "ur": "تونس",
+      "vi": "Tunisia",
+      "zh": "突尼斯"
+    },
+    "currency": [
+      "TND"
+    ],
+    "region": "Africa",
+    "subregion": "Northern Africa"
+  },
+  {
+    "callingCode": [
+      "90"
+    ],
+    "countryCode": "TR",
+    "countryName": "Turkey",
+    "countryNames": {
+      "cs": "Turecko",
+      "cy": "Turkey",
+      "de": "Türkei",
+      "en": "Turkey",
+      "es": "Turquía",
+      "et": "Türgi",
+      "fi": "Turkki",
+      "fr": "Turquie",
+      "hr": "Turska",
+      "it": "Turchia",
+      "ja": "トルコ",
+      "km": "Turkey",
+      "ko": "터키",
+      "my": "Turkey",
+      "nl": "Turkije",
+      "pl": "Turcja",
+      "pt": "Turquia",
+      "ru": "Турция",
+      "sk": "Turecko",
+      "th": "ตุรกี",
+      "ur": "ترکی",
+      "vi": "Thổ Nhĩ Kỳ",
+      "zh": "土耳其"
+    },
+    "currency": [
+      "TRY"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "993"
+    ],
+    "countryCode": "TM",
+    "countryName": "Turkmenistan",
+    "countryNames": {
+      "cs": "Turkmenistán",
+      "cy": "Turkmenistan",
+      "de": "Turkmenistan",
+      "en": "Turkmenistan",
+      "es": "Turkmenistán",
+      "et": "Türkmenistan",
+      "fi": "Turkmenistan",
+      "fr": "Turkménistan",
+      "hr": "Turkmenistan",
+      "it": "Turkmenistan",
+      "ja": "ト ルクメニスタン",
+      "km": "Turkmenistan",
+      "ko": "투르크메니스탄",
+      "my": "Turkmenistan",
+      "nl": "Turkmenistan",
+      "pl": "Turkmenistan",
+      "pt": "Turquemenistão",
+      "ru": "Туркмения",
+      "sk": "Turkménsko",
+      "th": "เติร์กเมนิสถาน",
+      "ur": "ترکمانستان",
+      "vi": "Turkmenistan",
+      "zh": "土库曼斯坦"
+    },
+    "currency": [
+      "TMT"
+    ],
+    "region": "Asia",
+    "subregion": "Central Asia"
+  },
+  {
+    "callingCode": [
+      "1649"
+    ],
+    "countryCode": "TC",
+    "countryName": "Turks and Caicos Islands",
+    "countryNames": {
+      "cs": "Turks a Caicos",
+      "cy": "Turks and Caicos Islands",
+      "de": "Turks-und Caicosinseln",
+      "en": "Turks and Caicos Islands",
+      "es": "Islas Turks y Caicos",
+      "et": "Turks ja Caicos",
+      "fi": "Turks-ja Caicossaaret",
+      "fr": "Îles Turques-et-Caïques",
+      "hr": "Otoci Turks i Caicos",
+      "it": "Isole Turks e Caicos",
+      "ja": "タークス・カイコス諸島",
+      "km": "Turks and Caicos Islands",
+      "ko": "터크스 케이커스  제도",
+      "my": "Turks and Caicos Islands",
+      "nl": "Turks-en Caicoseilanden",
+      "pl": "Turks i Caicos",
+      "pt": "Ilhas Turks e Caicos",
+      "ru": "Теркс и Кайкос",
+      "sk": "Turks a Caicos",
+      "th": "เติร์กและหมู่เกาะเคคอส",
+      "ur": "جزائر کیکس و ترکیہ",
+      "vi": "Quần đảo Turks và Caicos",
+      "zh": "特克斯和凯科斯群岛"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "688"
+    ],
+    "countryCode": "TV",
+    "countryName": "Tuvalu",
+    "countryNames": {
+      "cs": "Tuvalu",
+      "cy": "Tuvalu",
+      "de": "Tuvalu",
+      "en": "Tuvalu",
+      "es": "Tuvalu",
+      "et": "Tuvalu",
+      "fi": "Tuvalu",
+      "fr": "Tuvalu",
+      "hr": "Tuvalu",
+      "it": "Tuvalu",
+      "ja": "ツバル",
+      "km": "Tuvalu",
+      "ko": "투발루",
+      "my": "Tuvalu",
+      "nl": "Tuvalu",
+      "pl": "Tuvalu",
+      "pt": "Tuvalu",
+      "ru": "Тувалу",
+      "sk": "Tuvalu",
+      "th": "ตูวาลู",
+      "ur": "تووالو",
+      "vi": "Tuvalu",
+      "zh": "图瓦 卢"
+    },
+    "currency": [
+      "AUD"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "256"
+    ],
+    "countryCode": "UG",
+    "countryName": "Uganda",
+    "countryNames": {
+      "cs": "Uganda",
+      "cy": "Uganda",
+      "de": "Uganda",
+      "en": "Uganda",
+      "es": "Uganda",
+      "et": "Uganda",
+      "fi": "Uganda",
+      "fr": "Ouganda",
+      "hr": "Uganda",
+      "it": "Uganda",
+      "ja": "ウガンダ",
+      "km": "Uganda",
+      "ko": "우간다",
+      "my": "Uganda",
+      "nl": "Oeganda",
+      "pl": "Uganda",
+      "pt": "Uganda",
+      "ru": "Уганда",
+      "sk": "Uganda",
+      "th": "ยูกันดา",
+      "ur": "یوگنڈا",
+      "vi": "Uganda",
+      "zh": "乌干 达"
+    },
+    "currency": [
+      "UGX"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "380"
+    ],
+    "countryCode": "UA",
+    "countryName": "Ukraine",
+    "countryNames": {
+      "cs": "Ukrajina",
+      "cy": "Ukraine",
+      "de": "Ukraine",
+      "en": "Ukraine",
+      "es": "Ucrania",
+      "et": "Ukraina",
+      "fi": "Ukraina",
+      "fr": "Ukraine",
+      "hr": "Ukrajina",
+      "it": "Ucraina",
+      "ja": "ウクライナ",
+      "km": "Ukraine",
+      "ko": "우크라이나",
+      "my": "Ukraine",
+      "nl": "Oekraïne",
+      "pl": "Ukraina",
+      "pt": "Ucrânia",
+      "ru": "Укр аина",
+      "sk": "Ukrajina",
+      "th": "ยูเครน",
+      "ur": "یوکرین",
+      "vi": "Ukraine",
+      "zh": "乌克兰"
+    },
+    "currency": [
+      "UAH"
+    ],
+    "region": "Europe",
+    "subregion": "Eastern Europe"
+  },
+  {
+    "callingCode": [
+      "971"
+    ],
+    "countryCode": "AE",
+    "countryName": "United Arab Emirates",
+    "countryNames": {
+      "cs": "Spojené arabské emiráty",
+      "cy": "United Arab Emirates",
+      "de": "Vereinigte Arabische Emirate",
+      "en": "United Arab Emirates",
+      "es": "Emiratos Árabes Unidos",
+      "et": "Araabia Ühendemiraadid",
+      "fi": "Arabiemiraatit",
+      "fr": "Émirats arabes unis",
+      "hr": "Ujedinjeni Arapski Emirati",
+      "it": "Emirati Arabi Uniti",
+      "ja": "アラブ首長国連邦",
+      "km": "United Arab Emirates",
+      "ko": "아랍에미리트",
+      "my": "United Arab Emirates",
+      "nl": "Verenigde Arabische Emiraten",
+      "pl": "Zjednoczone Emiraty Arabskie",
+      "pt": "Emirados Árabes Unidos",
+      "ru": "Объединённые Араб ские Эмираты",
+      "sk": "Spojené arabské emiráty",
+      "th": "สหรัฐอาหรับเอมิเรตส์",
+      "ur": "متحدہ عرب امارات",
+      "vi": "Các Tiểu vương quốc Ả Rập Thống nhất",
+      "zh": "阿拉伯联合酋 长国"
+    },
+    "currency": [
+      "AED"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "44"
+    ],
+    "countryCode": "GB",
+    "countryName": "United Kingdom",
+    "countryNames": {
+      "cs": "Spojené království",
+      "cy": "United Kingdom",
+      "de": "Vereinigtes Königreich",
+      "en": "United Kingdom",
+      "es": "Reino Unido",
+      "et": "Suurbritannia",
+      "fi": "Yhdistynyt kuningaskunta",
+      "fr": "Royaume-Uni",
+      "hr": "Ujedinjeno Kraljevstvo",
+      "it": "Regno Unito",
+      "ja": "イギリス",
+      "km": "United Kingdom",
+      "ko": " 영국",
+      "my": "United Kingdom",
+      "nl": "Verenigd Koninkrijk",
+      "pl": "Zjednoczone Krłlestwo",
+      "pt": "Reino Unido",
+      "ru": "Великобрит  ания",
+      "sk": "Veľká Británia (Spojené kráľovstvo)",
+      "th": "สหราชอาณาจักร",
+      "ur": "مملکتِ متحدہ",
+      "vi": "Vương quốc Anh",
+      "zh": "英国"
+    },
+    "currency": [
+      "GBP"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  },
+  {
+    "callingCode": [
+      "1"
+    ],
+    "countryCode": "US",
+    "countryName": "United States",
+    "countryNames": {
+      "cs": "Spojené státy",
+      "cy": "United States",
+      "de": "Vereinigte Staaten",
+      "en": "United States",
+      "es": "Estados Unidos",
+      "et": "Ameerika Ühendriigid",
+      "fi": "Yhdysvallat",
+      "fr": "États-Unis",
+      "hr": "Sjedinjene Američke Države",
+      "it": "Stati Uniti d'America",
+      "ja": "アメリカ合衆国",
+      "km": "United States",
+      "ko": "미국",
+      "my": "United States",
+      "nl": "Verenigde Staten",
+      "pl": "Stany Zjednoczone",
+      "pt": "Estados Unidos",
+      "ru": " Соединённы е Штаты Америки",
+      "sk": "Spojené štáty americké",
+      "th": "สหรัฐอเมริกา",
+      "ur": "ریاستہائے متحدہ",
+      "vi": "Hoa Kỳ",
+      "zh": "美国"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "North America"
+  },
+  {
+    "callingCode": [
+      "1340"
+    ],
+    "countryCode": "VI",
+    "countryName": "United States Virgin Islands",
+    "countryNames": {
+      "cs": "Americké Panenské ostrovy",
+      "cy": "United States Virgin Islands",
+      "de": "Amerikanische Jungferninseln",
+      "en": "United States Virgin Islands",
+      "es": "Islas Vírgenes de los Estados Unidos",
+      "et": "Neitsisaared, USA",
+      "fi": "Neitsytsaaret",
+      "fr": "Îles Vierges des États-Unis",
+      "hr": "Američki Djevičanski Otoci",
+      "it": "Isole Vergini americane",
+      "ja": "アメリカ領ヴァージン諸島",
+      "km": "United States Virgin Islands",
+      "ko": "미국령 버진아일랜드",
+      "my": "United States Virgin Islands",
+      "nl": "Amerikaanse Maagdeneilanden",
+      "pl": "Wyspy Dziewicze Stanów Zjednoczonych",
+      "pt": "Ilhas Virgens dos Estados Unidos",
+      "ru": "Виргинские Острова",
+      "sk": "Americké Panenské ostrovy",
+      "th": "หมู่เกาะเวอร์จินอเมริกัน",
+      "ur": "امریکی جزائر ورجن",
+      "vi": "Quần đảo Virgin thuộc Mỹ",
+      "zh": "美属维尔京群岛"
+    },
+    "currency": [
+      "USD"
+    ],
+    "region": "Americas",
+    "subregion": "Caribbean"
+  },
+  {
+    "callingCode": [
+      "598"
+    ],
+    "countryCode": "UY",
+    "countryName": "Uruguay",
+    "countryNames": {
+      "cs": "Uruguay",
+      "cy": "Uruguay",
+      "de": "Uruguay",
+      "en": "Uruguay",
+      "es": "Uruguay",
+      "et": "Uruguay",
+      "fi": "Uruguay",
+      "fr": "Uruguay",
+      "hr": "Urugvaj",
+      "it": "Uruguay",
+      "ja": "ウルグアイ",
+      "km": "Uruguay",
+      "ko": "우루과이",
+      "my": "Uruguay",
+      "nl": "Uruguay",
+      "pl": "Urugwaj",
+      "pt": "Uruguai",
+      "ru": "Уругв ай",
+      "sk": "Uruguaj",
+      "th": "อุรุกวัย",
+      "ur": "یوراگوئے",
+      "vi": "Uruguay",
+      "zh": "乌拉圭"
+    },
+    "currency": [
+      "UYU"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "998"
+    ],
+    "countryCode": "UZ",
+    "countryName": "Uzbekistan",
+    "countryNames": {
+      "cs": "Uzbekistán",
+      "cy": "Uzbekistan",
+      "de": "Usbekistan",
+      "en": "Uzbekistan",
+      "es": "Uzbekistán",
+      "et": "Usbekistan",
+      "fi": "Uzbekistan",
+      "fr": "Ouzbékistan",
+      "hr": "Uzbekistan",
+      "it": "Uzbekistan",
+      "ja": "ウズベキスタン",
+      "km": "Uzbekistan",
+      "ko": "우즈베키스탄",
+      "my": "Uzbekistan",
+      "nl": "Oezbekistan",
+      "pl": "Uzbekistan",
+      "pt": "Uzbequistão",
+      "ru": "Узбекистан",
+      "sk": "Uzbekistan",
+      "th": "อุซเบกิสถาน",
+      "ur": "ازبکستان",
+      "vi": "Uzbekistan",
+      "zh": "乌兹别克斯坦"
+    },
+    "currency": [
+      "UZS"
+    ],
+    "region": "Asia",
+    "subregion": "Central Asia"
+  },
+  {
+    "callingCode": [
+      "678"
+    ],
+    "countryCode": "VU",
+    "countryName": "Vanuatu",
+    "countryNames": {
+      "cs": "Vanuatu",
+      "cy": "Vanuatu",
+      "de": "Vanuatu",
+      "en": "Vanuatu",
+      "es": "Vanuatu",
+      "et": "Vanuatu",
+      "fi": "Vanuatu",
+      "fr": "Vanuatu",
+      "hr": "Vanuatu",
+      "it": "Vanuatu",
+      "ja": "バヌアツ",
+      "km": "Vanuatu",
+      "ko": "바누아투",
+      "my": "Vanuatu",
+      "nl": "Vanuatu",
+      "pl": "Vanuatu",
+      "pt": "Vanuatu",
+      "ru": "Вануату",
+      "sk": "Vanuatu",
+      "th": "วานูอาตู",
+      "ur": "وانواتو",
+      "vi": "Vanuatu",
+      "zh": "瓦努阿图"
+    },
+    "currency": [
+      "VUV"
+    ],
+    "region": "Oceania",
+    "subregion": "Melanesia"
+  },
+  {
+    "callingCode": [
+      "3906698",
+      "379"
+    ],
+    "countryCode": "VA",
+    "countryName": "Vatican City",
+    "countryNames": {
+      "cs": "Vatikán",
+      "cy": "Vatican City",
+      "de": "Vatikanstadt",
+      "en": "Vatican City",
+      "es": "Ciudad del Vaticano",
+      "et": "Vatikan",
+      "fi": "Vatikaani",
+      "fr": "Cité du Vatican",
+      "hr": "Vatikan",
+      "it": "Città del Vaticano",
+      "ja": "バチカン市国",
+      "km": "Vatican City",
+      "ko": "바티칸",
+      "my": "Vatican City",
+      "nl": "Vaticaanstad",
+      "pl": "Watykan",
+      "pt": "Cidade do Vaticano",
+      "ru": "Ватикан",
+      "sk": "Vatikán",
+      "th": "วาติกัน",
+      "ur": "ویٹیکن سٹی",
+      "vi": "Vatican City",
+      "zh": "梵蒂冈"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Southern Europe"
+  },
+  {
+    "callingCode": [
+      "58"
+    ],
+    "countryCode": "VE",
+    "countryName": "Venezuela",
+    "countryNames": {
+      "cs": "Venezuela",
+      "cy": "Venezuela",
+      "de": "Venezuela",
+      "en": "Venezuela",
+      "es": "Venezuela",
+      "et": "Venezuela",
+      "fi": "Venezuela",
+      "fr": "Venezuela",
+      "hr": "Venezuela",
+      "it": "Venezuela",
+      "ja": "ベネズエラ・ボリバル共和国",
+      "km": "Venezuela",
+      "ko": "베네수엘라",
+      "my": "Venezuela",
+      "nl": "Venezuela",
+      "pl": "Wenezuela",
+      "pt": "Venezuela",
+      "ru": "Венесуэла",
+      "sk": "Venezuela",
+      "th": "เวเนซุเอลา",
+      "ur": "وینیزویلا",
+      "vi": "Venezuela",
+      "zh": "委内瑞拉"
+    },
+    "currency": [
+      "VEF"
+    ],
+    "region": "Americas",
+    "subregion": "South America"
+  },
+  {
+    "callingCode": [
+      "84"
+    ],
+    "countryCode": "VN",
+    "countryName": "Vietnam",
+    "countryNames": {
+      "cs": "Vietnam",
+      "cy": "Vietnam",
+      "de": "Vietnam",
+      "en": "Vietnam",
+      "es": "Vietnam",
+      "et": "Vietnam",
+      "fi": "Vietnam",
+      "fr": "Viêt Nam",
+      "hr": "Vijetnam",
+      "it": "Vietnam",
+      "ja": "ベトナム",
+      "km": "Vietnam",
+      "ko": "베트남",
+      "my": "Vietnam",
+      "nl": "Vietnam",
+      "pl": "Wietnam",
+      "pt": "Vietname",
+      "ru": "Вьетнам",
+      "sk": "Vietnam",
+      "th": "เวียดนาม",
+      "ur": "ویتنام",
+      "vi": "Việt Nam",
+      "zh": "越南"
+    },
+    "currency": [
+      "VND"
+    ],
+    "region": "Asia",
+    "subregion": "South-Eastern Asia"
+  },
+  {
+    "callingCode": [
+      "681"
+    ],
+    "countryCode": "WF",
+    "countryName": "Wallis and Futuna",
+    "countryNames": {
+      "cs": "Wallis a Futuna",
+      "cy": "Wallis and Futuna",
+      "de": "Wallis und Futuna",
+      "en": "Wallis and Futuna",
+      "es": "Wallis y Futuna",
+      "et": "Wallis ja Futuna",
+      "fi": "Wallis ja Futuna",
+      "fr": "Wallis-et-Futuna",
+      "hr": "Wallis i Fortuna",
+      "it": "Wallis e Futuna",
+      "ja": "ウォリス・フツナ",
+      "km": "Wallis and Futuna",
+      "ko": "",
+      "my": "Wallis and Futuna",
+      "nl": "Wallis en Futuna",
+      "pl": "Wallis i Futuna",
+      "pt": "Wallis e Futuna",
+      "ru": "Уоллис и Футуна",
+      "sk": "Wallis a Futuna",
+      "th": "หมู่เกาะวาลลิสและฟุตูนา",
+      "ur": "والس و فتونہ",
+      "vi": "Quần đảo Wallis và Futuna",
+      "zh": "瓦利斯和富图纳群岛"
+    },
+    "currency": [
+      "XPF"
+    ],
+    "region": "Oceania",
+    "subregion": "Polynesia"
+  },
+  {
+    "callingCode": [
+      "212"
+    ],
+    "countryCode": "EH",
+    "countryName": "Western Sahara",
+    "countryNames": {
+      "cs": "Západní Sahara",
+      "cy": "Western Sahara",
+      "de": "Westsahara",
+      "en": "Western Sahara",
+      "es": "Sahara Occidental",
+      "et": "Lääne-Sahara",
+      "fi": "Länsi-Sahara",
+      "fr": "Sahara Occidental",
+      "hr": "Zapadna Sahara",
+      "it": "Sahara Occidentale",
+      "ja": "西サハラ",
+      "km": "Western Sahara",
+      "ko": "서사하라",
+      "my": "Western Sahara",
+      "nl": "Westelijke Sahara",
+      "pl": "Sahara Zachodnia",
+      "pt": "Saara Ocidental",
+      "ru": "Западн ая  Сахара",
+      "sk": "Západná Sahara",
+      "th": "West Sahara",
+      "ur": "مغربی صحارا",
+      "vi": "Tây Sahara",
+      "zh": "西撒哈拉"
+    },
+    "currency": [
+      "MAD",
+      "DZD",
+      "MRO"
+    ],
+    "region": "Africa",
+    "subregion": "Northern Africa"
+  },
+  {
+    "callingCode": [
+      "967"
+    ],
+    "countryCode": "YE",
+    "countryName": "Yemen",
+    "countryNames": {
+      "cs": "Jemen",
+      "cy": "Yemen",
+      "de": "Jemen",
+      "en": "Yemen",
+      "es": "Yemen",
+      "et": "Jeemen",
+      "fi": "Jemen",
+      "fr": "Yémen",
+      "hr": "Jemen",
+      "it": "Yemen",
+      "ja": "イエメン",
+      "km": "Yemen",
+      "ko": "예멘",
+      "my": "Yemen",
+      "nl": "Jemen",
+      "pl": "Jemen",
+      "pt": "Iémen",
+      "ru": "Йемен",
+      "sk": "Jemen",
+      "th": "เยเมน",
+      "ur": "یمن",
+      "vi": "Yemen",
+      "zh": "也门"
+    },
+    "currency": [
+      "YER"
+    ],
+    "region": "Asia",
+    "subregion": "Western Asia"
+  },
+  {
+    "callingCode": [
+      "260"
+    ],
+    "countryCode": "ZM",
+    "countryName": "Zambia",
+    "countryNames": {
+      "cs": "Zambie",
+      "cy": "Zambia",
+      "de": "Sambia",
+      "en": "Zambia",
+      "es": "Zambia",
+      "et": "Sambia",
+      "fi": "Sambia",
+      "fr": "Zambie",
+      "hr": "Zambija",
+      "it": "Zambia",
+      "ja": "ザンビア",
+      "km": "Zambia",
+      "ko": "잠비아",
+      "my": "Zambia",
+      "nl": "Zambia",
+      "pl": "Zambia",
+      "pt": "Zâmbia",
+      "ru": "Замбия",
+      "sk": "Zambia",
+      "th": "แซมเบีย",
+      "ur": "زیمبیا",
+      "vi": "Zambia",
+      "zh": "赞比亚"
+    },
+    "currency": [
+      "ZMW"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "263"
+    ],
+    "countryCode": "ZW",
+    "countryName": "Zimbabwe",
+    "countryNames": {
+      "cs": "Zimbabwe",
+      "cy": "Zimbabwe",
+      "de": "Simbabwe",
+      "en": "Zimbabwe",
+      "es": "Zimbabue",
+      "et": "Zimbabwe",
+      "fi": "Zimbabwe",
+      "fr": "Zimbabwe",
+      "hr": "Zimbabve",
+      "it": "Zimbabwe",
+      "ja": "ジンバブ エ",
+      "km": "Zimbabwe",
+      "ko": "짐바브 웨",
+      "my": "Zimbabwe",
+      "nl": "Zimbabwe",
+      "pl": "Zimbabwe",
+      "pt": "Zimbabwe",
+      "ru": "Зимбабве",
+      "sk": "Zimbabwe",
+      "th": "ซิมบับเว",
+      "ur": "زمبابوے",
+      "vi": "Zimbabwe",
+      "zh": "津巴布韦"
+    },
+    "currency": [
+      "ZWL"
+    ],
+    "region": "Africa",
+    "subregion": "Eastern Africa"
+  },
+  {
+    "callingCode": [
+      "358"
+    ],
+    "countryCode": "AX",
+    "countryName": "Åland Islands",
+    "countryNames": {
+      "cs": "Ålandy",
+      "cy": "Åland Islands",
+      "de": "Åland",
+      "en": "Åland Islands",
+      "es": "Alandia",
+      "et": "Ahvenamaa",
+      "fi": "Ahvenanmaa",
+      "fr": "Ahvenanmaa",
+      "hr": "Ålandski otoci",
+      "it": "Isole Aland",
+      "ja": "オーランド諸島",
+      "km": "Åland Islands",
+      "ko": "올란드 제도",
+      "my": "Åland Islands",
+      "nl": "Ålandeilanden",
+      "pl": "Wyspy Alandzkie",
+      "pt": "Alândia",
+      "ru": "Аландские о строва",
+      "sk": "Alandy",
+      "th": "หมู่เกาะโอลันด์",
+      "ur": "جزائر اولند",
+      "vi": "Quần đảo Åland",
+      "zh": "奥兰群岛"
+    },
+    "currency": [
+      "EUR"
+    ],
+    "region": "Europe",
+    "subregion": "Northern Europe"
+  }
+]

+ 167 - 0
Strides-APP/app/i18n/index.js

@@ -0,0 +1,167 @@
+import app from '../../app.json';
+import I18n from 'react-native-i18n';
+import en from './locales/en';
+import es from './locales/es';
+import de from './locales/de';
+import fr from './locales/fr';
+import ja from './locales/ja';
+import km from './locales/km';
+import ko from './locales/ko';
+import my from './locales/my';
+import th from './locales/th';
+import vi from './locales/vi';
+import zh from './locales/zh';
+import tw from './locales/zh-TW';
+import { getStorage } from '../utils/storage';
+
+I18n.fallbacks = true;
+//I18n.l("currency", 630, {})
+//I18n.t("", {})
+const SETTING_KEY = "locale-settings";
+const DEBUG = app.debug && !app.product;
+const LOCALES = [{
+  name: en.name,
+  value: "en",//英语
+  enable: true
+}, {
+  name: de.name,
+  value: "de",//德语
+  enable: DEBUG
+}, {
+  name: es.name,
+  value: "es",//西语
+  enable: DEBUG
+}, {
+  name: fr.name,
+  value: "fr",//法语
+  enable: DEBUG
+}, {
+  name: ja.name,
+  value: "ja",//日语
+  enable: DEBUG
+}, {
+  name: ko.name,
+  value: "ko",//韩语
+  enable: DEBUG
+}, {
+  name: km.name,
+  value: "km",//柬埔寨-高棉语
+  enable: DEBUG
+}, {
+  name: my.name,
+  value: "my",//缅甸语
+  enable: DEBUG
+}, {
+  name: th.name,
+  value: "th",//泰语
+  enable: DEBUG
+}, {
+  name: vi.name,
+  value: "vi",//越南语
+  enable: DEBUG
+}, {
+  name: zh.name,
+  value: "zh-CN",//简体中文
+  enable: true
+}, {
+  name: tw.name,
+  value: "zh-HK",//繁體中文
+  enable: true
+}]
+if (app.modules.i18n) {
+  I18n.translations = {
+    default: en,
+    en, es, de, fr,
+    ja, km, ko, my,
+    th, vi, zh, 
+    "zh-CN": zh,
+    "zh-HK": tw
+  }
+
+  global.defaultLocale = I18n.currentLocale();
+  if (app.debug)
+    console.log("[I18n] defaultLocal", global.defaultLocale);
+} else {
+  I18n.translations = {
+    default: en,
+    en
+  }
+  global.defaultLocale = "en";
+}
+const getLocaleName = () => {
+  var locale = ""
+  for (let l of LOCALES) {
+    if (l.value == defaultLocale) {
+      locale = l.value;
+      break;
+    }
+  }
+  if (!locale) {
+    if (defaultLocale.indexOf("zh") >= 0) {
+      if (defaultLocale.indexOf("CN") >= 0) {
+        locale = "zh-CN"
+      } else {
+        locale = "zh-HK"
+      }
+    } else {
+      for (let l of LOCALES) {
+        if (defaultLocale.indexOf(l.value) >= 0) {
+          locale = l.value;
+          break;
+        }
+      }
+    }
+    if (app.debug)
+      console.log("[I18n] getLocaleName(2)", locale);
+  } else {
+    if (app.debug)
+      console.log("[I18n] getLocaleName(1)", locale);
+  }
+  return locale;
+}
+
+global.$t = (scope) => {
+  return I18n.t(scope);
+}
+
+const init = (back) =>  {
+  if (app.modules.i18n) {
+    getStorage(SETTING_KEY).then(res => {
+      if (res) {
+        global.currentLocale = I18n.locale = res;
+      } else {
+        global.currentLocale = getLocaleName();//"zh-CN"
+        I18n.locale = global.currentLocale;//设置local
+      }
+      if (app.debug) console.log("[I18n] currentLocale", global.currentLocale, I18n.locale);
+      back();
+    })
+  } else {
+    global.currentLocale = "en";
+    back();
+  }
+}
+
+
+export const i18nUtil = {
+  init: init,
+  locales: LOCALES,
+  SETTING_KEY: SETTING_KEY,
+  getLocaleName: getLocaleName,
+  isChinese: () => global.currentLocale.indexOf("zh") >= 0,
+  getLocale: () => global.currentLocale,
+  setlocale: (localeName) => {
+    global.currentLocale = I18n.locale = localeName;
+    global.ComCountryList = undefined;
+  },
+  getDefaultLocale: () => global.defaultLocale,
+  analyzeLocaleData: (data, def) => {
+    for (let l in data) {
+      //console.log(l);
+      if (currentLocale.indexOf(l) >= 0) {
+        return data[l]
+      }
+    }
+    return def ?? data;
+  }
+}

+ 7 - 0
Strides-APP/app/i18n/locales/de.js

@@ -0,0 +1,7 @@
+/**
+ * 德国语言包
+ */
+ export default {
+  name: "Deutsch",
+  default: "Default",
+}

+ 417 - 0
Strides-APP/app/i18n/locales/en.js

@@ -0,0 +1,417 @@
+/**
+ * 英文语言包
+ */
+export default {
+  name: "English",
+  default: "Default",
+  nav: {
+    ok: "OK",
+    no: "NO",
+    yes: "YES",
+    back: "BACK",
+    view: "VIEW",
+    confirm: "CONFIRM",
+    cancel: "CANCEL",
+    submit: "SUBMIT",
+    loading: "Loading...",
+    waiting: "Waiting..."
+  },
+  common: {
+    no: "No",
+    yes: "Yes",
+    back: "Back",
+    save: "Save",
+    error: "Error",
+    confirm: "Confirm",
+    cancel: "Cancel",
+    close: "Close",
+    select: "Select",
+    inputAddText: "Add text",
+    cropperTitle: "Cropper",
+    addFailed: "Add failed, please retry",
+    addSuccess: "Add successfully!",
+    updateFailed: "Update failed, please retry",
+    updateSuccess: "Update successfully!",
+    cancelFailed: "Cancel failed, please retry",
+    cancelSuccess: "Cancel successfully!",
+    uploadFailed: "Upload failed, please retry",
+    uploadSuccess: "Upload successfully!",
+    deleteFailed: "Delete failed, please retry",
+    deleteSuccess: "Delete successfully!",
+    submitSuccess: "Submit successfully!",
+    pulldown2Refresh: "Pulldown to Refresh"
+  },
+  route: {
+    about: "About",
+    addCards: "Add Cards",
+    addVehicle: "Add Vehicle",
+    bookmarks: "Bookmarks",
+    changePassword: "Account Security",
+    charging: "Charging",
+    chargingSite: "Charging Site",
+    driverRegister: "Fleet / PHV Registration",
+    editAddress: "Edit Address",
+    editProfile: "My Profile",
+    editVehicle: "Update Vehicle",
+    feedback: "Feedback",
+    forgotPassword: "Forgot Password",
+    makePayment: "Make Payment",
+    myVehicles: "My Vehicles",
+    notificationTest: "Notification Test",
+    paymentMethod: "Payment Method",
+    paynow: "PAYNOW",
+    payPerUse: "Pay Per Use",
+    privacyPolicy: "Privacy Policy",
+    profileSettings: "Profile Settings",
+    publicRegister: "Public Registration",
+    qrScan: "QR Scan",
+    rating: "Your Rating",
+    referral: "Referral",
+    search: "Search",
+    settings: "Settings",
+    summary: "Summary",
+    termsOfUse: "Terms of Use",
+    topUp: "Purchase Credits",
+    topUpWithCard: "Top Up with Card",
+    wallet: "Transactions"
+  },
+  sign: {
+    aConfirmationEmailTo: "a confirmation email has been sent to",
+    agreePDVInfoAccurate: "I agree that the information that i’m submitting is the latest and accurate.",
+    back2Login: "Back to Login",
+    btnLogin: "LOGIN",
+    btnSendOTP: "EMAIL OTP",
+    btnSignUp: "SIGN UP",
+    errContactNoFormat: "Phone Number is incorrect format",
+    errEmailFormat: "Email is incorrect format",
+    errPasswordConfirm: "The twice passwords are inconsistent",
+    errPasswordStrong: "Password is not strong",
+    forgotPassword: "Forgot Password",
+    hintPDVLicence: "PH Driver Vocational Licence",
+    iHaveReadAndAgree: "I have read and I agree with the ",
+    labelCompany: "Company",
+    labelConfirmPassword: "Confirm Password",
+    labelCountry: "Country",
+    labelCreatePassword: "Create Password",
+    labelDisplayName: "Display Name",
+    labelE_mail: "E-mail",
+    labelEmail: "Email Address",
+    labelMobileNumber: "Mobile Number",
+    labelNewPassword: "New Password",
+    labelOtp: "OTP",
+    labelPassword: "Password",
+    labelPDVLicence: "PDV Licence",
+    labelPDVPhotos: "  PDV Photos\n(Front & Back)",
+    labelPhoneNumber: "Phone Number",
+    labelValidateEmail: "Validate Email",
+    labelYourCompany: "Your Company",
+    linkAndLink: "and ",
+    linkAndLinkEnd: ".",
+    password8more: "8 or more characters",
+    passwordLeastNumber: "at least one number",
+    passwordLeastNumberAndSpecial: "at least one number and one special character",
+    passwordMustHave: "Your Password Must Have:",
+    passwordStrength: "Password Strength",
+    passwordUpperLower: "upper and lower case letters",
+    plsInputContactNo: "Please enter contact number",
+    plsInputDiaplayName: "Please enter display name",
+    plsInputEmail: "Please enter email address",
+    plsInputOTP: "Please enter verification code",
+    plsInputPassword: "Please enter password",
+    plsInputPassword2: "Please enter confirm password",
+    plsInputPDVLicence: "Please enter PDV Licence",
+    plsLoginTitle: "Please Login",
+    plsUploadLicencePhotos: "Please upload PDV Licence Photos",
+    registerDriverUser: "Register as Fleet / PHV Driver",
+    registerPublicUser: "Register as Public User",
+    rememberMe: "Remember me",
+    resetPasswordSuccess: "Reset password successfully",
+    sendOTPSuccess: "Send verification code successfully",
+    signIn: "Sign In",
+    signUpSuccess: "Sign up successfully!",
+    thanksRegisterWith: "Thank you for registering with",
+    tipNewUser: "New User?",
+    titleFleetRegistration: "Fleet / PHV Registration"
+  },
+  home: {
+    all: "All",
+    chargeable: "Chargeable",
+    chooseActivationType: "Choose Activation Type",
+    chooseConnecterType: "Choose Connecter Type",
+    chooseParkingFee: "Choose Parking Fee",
+    chooseProvider: "Choose Provider",
+    done: "Done",
+    filterResults: "Results",
+    filters: "Filters",
+    free: "Free",
+    later: "LATER",
+    locationPermissionTips: "Please grant location permission to obtain more service",
+    myLocation: "My Location",
+    newVersionName: "New Version: ",
+    noSearch: "No search result",
+    search: "Search",
+    searchHint: "Search using site name or service provider",
+    statusPrivate: "Private Site",
+    updateNow: "UPDATE NOW",
+    versionUpdate: "Version Update"
+  },
+  drawer: {
+    sign: "Sign In",
+    charging: "Charging",
+    wallet: "Transactions",
+    topup: "Credits",
+    feedback: "Feedback",
+    settings: "Settings",
+    about: "About Us",
+    logging: "Logging...",
+    privacyPolicy: "Privacy Policy",
+    termsOfUse: "Terms of Use",
+    mapsTest: "Maps Test",
+    debugOnly: "Debug Mode Options Only",
+    noChargingSession: "No charging session found"
+  },
+  profile: {
+    address: "Address",
+    addVehicle: "Add Vehicle",
+    apply2Fleet: "Apply as PHV/Fleet",
+    avatar: "Avatar",
+    chooseConnecterType: "Choose Connecter Type",
+    confirmDeleteAccount: "Are you sure you want to delete your account? This operation cannot be revoke.",
+    deleteAccount: "Delete Account",
+    deleteAccountSuccess: "Successfully deleted!",
+    email: "Email",
+    errPhoneNumberFormat: "Phone Number is incorrect format",
+    hintPostal: "Postal Code",
+    hintStreet: "Street name and number",
+    hintUnitNo: "Unit number",
+    labelCity: "City",
+    licensePlate: "License Plate",
+    logout: "Logout",
+    msgInputBrand: "Please type brand",
+    msgInputModel: "Please type model",
+    msgInputPlate: "Please type license plate",
+    myVehicles: "My Vehicles: ",
+    nickname: "Nickname",
+    notificationSettings: "Notification Settings",
+    noVehicles: "No Vehicles",
+    phoneNumber: "Phone Number",
+    plsInputCity: "Please input city name",
+    plsInputPostal: "Please input postal code",
+    plsInputStreet: "Please input street",
+    plsInputUnitNo: "Please input unit number",
+    postalCode: "Postal Code",
+    registerDate: "Registration Date",
+    remove: "Remove",
+    removeVehicle: "Remove Vehicle",
+    signOut: "Sign out",
+    street: "Street",
+    tipRemoveVehicle: "Are you sure you want to remove this vehicle?",
+    tipSignOut: "Are you sure you want to sign out?",
+    unitNumber: "Unit Number",
+    update: "Update",
+    updateNickname: "Update Nickname",
+    updatePhoneNumber: "Update Phone Number",
+    updateSuccess: "Update profile successfully",
+    userType: "User Type",
+    vehicleBrand: "Brand",
+    vehicleModel: "Model"
+  },
+  charging: {
+    AC: "AC",
+    DC: "DC",
+    Chademo: "CHAdeMO",
+    acChargers: "AC Chargers",
+    additionalInfo: "Additional Info",
+    additionalInformation: "Additional Information",
+    btnAuthenticate: "AUTHENTICATE",
+    btnCancelReservation: "Cancel Reservation",
+    btnComplete: "COMPLETE",
+    btnIntoCharging: "Into Charging",
+    btnOkay: "Okay",
+    btnReserve: "Reserve",
+    btnStartCharging: "START CHARGING",
+    btnStopCharging: "STOP CHARGING",
+    cancleReservation: "Cancle Reservation",
+    chargingInProgress: "Charging In Progress...",
+    chooseConnector: "Choose Connector",
+    chooseRate: "Choose Rate",
+    comfirmCancleReservation: "Are you sure you want to cancle reservation?",
+    confirmStopCharging: "Are you sure stop charging?",
+    dcChargers: "DC Chargers",
+    enterStationId: "Enter Station ID",
+    errAuthenticationError: "There seems to be an authentication error! Please try again",
+    errDetected: "An error detected, please retry.",
+    errNotChargeE0: "Your vehicle doesn’t seem to be charging. Please check your vehicle. (E0)",
+    errNotConnected: "Your vehicle is not connected to the charging station. Please check the connector.",
+    errUnable2Charge: "The charging station is unable to charge your vehicle.Please reauthenticate.",
+    errUnable2Reserved: "The charging station is reserved and unable to charge your vehicle.",
+    free: "Free",
+    hintExampleStationId: "e.g: LEMOC0002-1",
+    labelAvailableTotal: "Available/Total",
+    labelPower: "Power",
+    labelRate: "Rate",
+    labelStatus: "Status",
+    labelTimeElapsed: "Time Elapsed",
+    labelTotalCharges: "Total Charges",
+    labelTotalkWh: "Total kWh Delivered",
+    labelType: "Type",
+    lastUpdatedAt: "Last updated at ",
+    noRates: "No Rates",
+    numberAvailable: " available",
+    operatingHours: "Operating Hours",
+    parkingFees: "Parking Fees",
+    plsInputStationId: "Please input Station ID",
+    plsSelectConnnector: "Please select a connnector",
+    pressStartToBegin: "Press Start To Begin",
+    pullDownRefresh: "Pull down to refresh",
+    ratesPrivateNote: "NOTE: The charging stations are for private usage.",
+    reservedSuccess: "Reserved successfully!",
+    reserveTimeLeft: "Reservation time left",
+    scanQR: "Scan QR",
+    selectedCharger: "Selected Charger",
+    paymentMethod: "Payment Method",
+    selectPaymentMethod: "Select Payment Method",
+    siteAddress: "Address",
+    siteName: "Site Name",
+    statusAuthenticated: "Authenticated",
+    statusAvailable: "Available",
+    statusCharging: "Charging",
+    statusInitiating: "Initiating...",
+    statusNotConnected: "Not Connected",
+    statusPreparing: "Preparing",
+    statusPrivate: "Private",
+    statusUnavailable: "Unavailable",
+    statusInCharging: "In Charging",
+    stepAuthenticated: "Authenticated",
+    stepAuthenticatedDesc: "Press START CHARGING to begin.",
+    stepAuthenticating: "Authenticating",
+    stepAuthenticatingDesc: "Please wait while we authenticate your connection.",
+    stepChargingDesc: "Your vehicle is charging.\nPress STOP CHARGING to end charge",
+    stepInitializing: "Initializing",
+    stepInitializingDesc: "Please wait while we prepare to start charging...",
+    stepStoppingCharge: "Stopping Charge",
+    stepStoppingChargeDesc: "Please wait while we stop your charging...",
+    stepInsertConnector: "Insert Connector",
+    stepInsertConnectorDesc: "Insert Vehicle to Charger and press Authenticate.",
+    tabCharge: "CHARGE",
+    tabExplore: "Explore",
+    tabInfo: "INFO",
+    tabReserve: "Reserve",
+    tipsDisconnectConnector: "Please disconnect and return connector to charging station",
+    tipsRatesTax: "All rates Include 8% GST",
+    titleOops: "Oops!",
+    titleStopCharging: "Stop Charging",
+    toBeUpdated: "To be updated",
+    unallowReservation: "Reservation is not available for this site.",
+    unitHr: " hr ",
+    unitMin: " min"
+  },
+  wallet: {
+    atAglance: "At a glance",
+    averageCharge: "Average Charge",
+    averageSpend: "Average Spend",
+    averageTime: "Average Time",
+    balance: "Balance",
+    btnPurchase: "Purchase",
+    btnTop_Up: "Top-Up",
+    creditIsBelow5: "Your credit is below S$5.",
+    creditWallet: "Credits",
+    creditWalletLabel: "Credits: ",
+    forWeekOf: "Summary for the week",
+    labelBreakdown: "Breakdown",
+    labelChargeDelivered: "Charge Delivered:",
+    labelChargeRates: "Charge Rates (GST Inclusive):",
+    labelChargeTime: "Charge Time:",
+    labelDateTime: "Date Time:",
+    labelExchangeRate: "Exchange Rate:",
+    labelFinalPayment: "Final Payment:",
+    labelIdleFee: "Idle Fee:",
+    labelPayment: "Payment (GST Inclusive):",
+    labelPaymentMadeBy: "Payment Made By:",
+    labelPreviousBalance: "Previous Balance:",
+    labelReferenceId: "Reference ID:",
+    labelReservationFee: "Reservation Fee:",
+    labelResultingBalance: "Resulting Balance:",
+    labelStationId: "Station ID: ",
+    labelSubtotal: "Subtotal",
+    labelTransactionId: "Transaction ID: ",
+    labelYourConnector: "Your Connector",
+    labelYourStation: "Your Station",
+    linkSubmitFeedback: "Submit Your Feedback",
+    noHistoryData: "No data",
+    perHrWeek: "Hr/Week",
+    perWeek: "Week",
+    purchaseCredits: "Purchase",
+    statistics4HalfYear: "Statistics for the past 6 months",
+    statistics4week: "Statistics for this week",
+    tabHistory: "History",
+    tabOverview: "Overview",
+    tipsLowCredits: "This charging session will end if there is insufficient credits.\n",
+    tipsReceipt: "A Receipt Will Be Sent To Your Email",
+    titleChooseCreditValue: "Choose Credit Value",
+    titleChoosePaymentType: "Choose Payment Type",
+    titleLowCredits: "Low Credits",
+    topUp: "Top Up",
+    viewHistory: "View History"
+  },
+  payment: {
+    btnSave2gallery: "Save QR Code to Gallery",
+    btnSetting: "SETTING",
+    errInputCardNo: "Please type a correct card number",
+    errInputCvv: "Please type a correct CVV",
+    errInputTill: "Please type a correct valid till",
+    errSave2gallery: "Save to gallery failed",
+    errSave2galleryPermission: "Can not save images, Please grant storage or gallery permissions.",
+    hintCardName: "Your name on card",
+    hintValidTill: "MM/YY",
+    labelAmount: "Amount:",
+    labelCardName: "Name on Card",
+    labelCardNo: "Card Number",
+    labelChooseAmount: "Choose Amount:",
+    labelPayWith: "Pay with:",
+    labelTopUpAmount: "Top Up Amount:",
+    labelValidTill: "Valid Till",
+    openInBrowser: "Open in Browser",
+    paymentCompleted: "Payment Completed",
+    paymentOption: "Payment Option",
+    payPerUse: "Pay Per Use",
+    plsInputCardName: "Please type name on card",
+    plsInputCardNo: "Please type card number",
+    plsInputCvv: "Please type CVV",
+    plsInputTill: "Please type valid till",
+    successSave2gallery: "Save to gallery successfully",
+    tipsPayPerUse: "Un-utilized amount will be refunded.",
+    tipsPayPerUseCharge: "Once the payment is received, the charging will start automatically.",
+    tipsPayPerUseSave: "Please save the QR Code, and use your bank/eWallet app to scan the QR Code.",
+    tipsQrPayment: "Once the payment is completed, the top up amount will be credited into your credits automatically.",
+    tipsQrPaymentSave: "Please save the QR Code, and use your bank app to scan the QR Code."
+  },
+  settings: {
+    autoRefreshInterval: "Auto refresh interval",
+    language: "Language",
+    maps: "Maps",
+    notification: "Notification",
+    notifyChargingComplete: "Notify me when charging complete",
+    notifyLowBalance: "Notify me when credits is low balance",
+    notifyPromotionsOffers: "Notify me for promotions and offers",
+    refreshInterval: "Refresh interval",
+    seconds: "s",
+    showMyLocations: "Always show my locations"
+  },
+  feedback: {
+    errFeedbackType: "Please select type of feedback",
+    errFeednackContent: "Please type feedback content",
+    errFetchType: "Can not fetch feedback type!",
+    labelConnector: "Which connecter?",
+    labelContent: "Please fill in here (500 words left)",
+    labelStation: "Which charging station?",
+    labelUpload: "Please upload relevant images",
+    searchingChargeBox: "Searching for ChargeBoxId",
+    selectConnector: "Select a Connecter",
+    sendSuccess: "Send feedback successfully!",
+    submitFeedback: "Submit Feedback",
+    tipsLetKnow: "Please let us know below!",
+    tipsSomething: "Have something to tell us?",
+    typeOfFeedback: "Type of Feedback"
+  }
+}

+ 7 - 0
Strides-APP/app/i18n/locales/es.js

@@ -0,0 +1,7 @@
+/**
+ * 法国语言包
+ */
+ export default {
+  name: "Español",
+  default: "Predeterminado",
+}

+ 7 - 0
Strides-APP/app/i18n/locales/fr.js

@@ -0,0 +1,7 @@
+/**
+ * 法国语言包
+ */
+ export default {
+  name: "Français",
+  default: "Défaut",
+}

+ 7 - 0
Strides-APP/app/i18n/locales/ja.js

@@ -0,0 +1,7 @@
+/**
+ * 日本语言包
+ */
+ export default {
+  name: "日本語",
+  default: "デフォルト",
+}

+ 7 - 0
Strides-APP/app/i18n/locales/km.js

@@ -0,0 +1,7 @@
+/**
+ * 柬埔寨-高棉语言包
+ */
+ export default {
+  name: "ភាសាខ្មែរ",
+  default: "លំនាំដើម",
+}

+ 7 - 0
Strides-APP/app/i18n/locales/ko.js

@@ -0,0 +1,7 @@
+/**
+ * 韩国语言包
+ */
+ export default {
+  name: "한국어",
+  default: "기본",
+}

+ 7 - 0
Strides-APP/app/i18n/locales/my.js

@@ -0,0 +1,7 @@
+/**
+ * 缅甸语言包
+ */
+ export default {
+  name: "ဗာရမ်",
+  default: "စံထားချက်",
+}

+ 7 - 0
Strides-APP/app/i18n/locales/th.js

@@ -0,0 +1,7 @@
+/**
+ * 泰文语言包
+ */
+ export default {
+  name: "ไทย",
+  default: "ค่าปริยาย",
+}

+ 7 - 0
Strides-APP/app/i18n/locales/vi.js

@@ -0,0 +1,7 @@
+/**
+ * 越南语言包
+ */
+ export default {
+  name: "Tiếng Việt",
+  default: "Mặc định",
+}

+ 417 - 0
Strides-APP/app/i18n/locales/zh-TW.js

@@ -0,0 +1,417 @@
+/**
+ * 繁體中文語言包
+ */
+ export default {
+  name: "繁體中文",
+  default: "跟隨系統",
+  nav: {
+    ok: "確定",
+    no: " 否 ",
+    yes: " 是 ",
+    back: "返回",
+    view: "查看",
+    confirm: "確認",
+    cancel: "取消",
+    submit: "提交",
+    loading: "加載中...",
+    waiting: "請稍後..."
+  },
+  common: {
+    no: "否",
+    yes: "是",
+    back: "返回",
+    save: "儲存",
+    error: "錯誤",
+    confirm: "確認",
+    cancel: "取消",
+    close: "關閉",
+    select: "請選擇",
+    inputAddText: "請輸入",
+    cropperTitle: "裁剪",
+    addFailed: "添加失敗,請重試",
+    addSuccess: "添加成功",
+    updateFailed: "更新失敗,請重試",
+    updateSuccess: "更新成功",
+    cancelFailed: "取消失敗,請重試",
+    cancelSuccess: "取消成功",
+    uploadFailed: "上傳失敗,請重試",
+    uploadSuccess: "上傳成功",
+    deleteFailed: "刪除失敗,請重試",
+    deleteSuccess: "刪除成功",
+    submitSuccess: "提交成功",
+    pulldown2Refresh: "下拉以刷新"
+  },
+  route: {
+    about: "關于",
+    addCards: "添加信用卡",
+    addVehicle: "添加車輛",
+    bookmarks: "我的收藏",
+    changePassword: "修改密碼",
+    chargingSite: "充電站詳情",
+    charging: "充電",
+    driverRegister: "車隊注冊",
+    editAddress: "編輯地址",
+    editProfile: "個人設定",
+    editVehicle: "修改車輛訊息",
+    feedback: "反饋",
+    forgotPassword: "忘記密碼",
+    makePayment: "收銀台",
+    myVehicles: "我的車輛",
+    notificationTest: "Firebase通知測試",
+    paymentMethod: "付款方式",
+    paynow: "PAYNOW",
+    payPerUse: "按次付費",
+    privacyPolicy: "隱私權政策",
+    profileSettings: "個人訊息",
+    publicRegister: "公共用戶注冊",
+    qrScan: "掃壹掃",
+    rating: "評價",
+    referral: "邀請",
+    search: "搜尋",
+    settings: "設定",
+    summary: "交易詳情",
+    termsOfUse: "使用條款",
+    topUp: "餘額充值",
+    topUpWithCard: "使用信用卡充值",
+    wallet: "餘額"
+  },
+  sign: {
+    aConfirmationEmailTo: "我們已向以下信箱發送壹封確認電子郵件",
+    agreePDVInfoAccurate: "我同意我提交的訊息是最新且真實的",
+    back2Login: "去登入",
+    btnLogin: "登入",
+    btnSendOTP: "獲取校驗碼",
+    btnSignUp: "注冊",
+    errContactNoFormat: "電話號碼格式不正確",
+    errEmailFormat: "信箱格式不正確",
+    errPasswordConfirm: "兩此輸入密碼不壹致",
+    errPasswordStrong: "密碼強度不符合規則",
+    forgotPassword: "忘記密碼",
+    hintPDVLicence: "PH/PDV駕照號碼",
+    iHaveReadAndAgree: "我已閱讀並同意《",
+    labelCompany: "公司",
+    labelConfirmPassword: "確認密碼",
+    labelCountry: "國家",
+    labelCreatePassword: "創建密碼",
+    labelDisplayName: "名字",
+    labelE_mail: "信箱",
+    labelEmail: "信箱",
+    labelMobileNumber: "手機號碼",
+    labelNewPassword: "新密碼",
+    labelOtp: "驗證碼",
+    labelPassword: "密碼",
+    labelPDVLicence: "PDV駕駛證號",
+    labelPDVPhotos: " 駕駛證照片\n(正面&背面)",
+    labelPhoneNumber: "手機號碼",
+    labelValidateEmail: "信箱驗證碼",
+    labelYourCompany: "妳的公司",
+    linkAndLink: "》《",
+    linkAndLinkEnd: "》",
+    password8more: "8個以上字母",
+    passwordLeastNumber: "至少包含壹個數字",
+    passwordLeastNumberAndSpecial: "至少包含壹個數字和壹個特殊字符",
+    passwordMustHave: "您的密碼須包含以下字符:",
+    passwordStrength: "密碼強度",
+    passwordUpperLower: "包含大寫和小寫字母",
+    plsInputContactNo: "請輸入電話號碼",
+    plsInputDiaplayName: "請輸入您的名字",
+    plsInputEmail: "請輸入電子信箱地址",
+    plsInputOTP: "請輸入驗證碼",
+    plsInputPassword: "請輸入密碼",
+    plsInputPassword2: "請輸入確認密碼",
+    plsInputPDVLicence: "請輸入PDV駕駛證號",
+    plsLoginTitle: "請登⼊",
+    plsUploadLicencePhotos: "請上傳PDV駕駛證照片",
+    registerDriverUser: "注冊成爲爲車隊/PHV司機用戶",
+    registerPublicUser: "注冊爲公共用戶",
+    rememberMe: "記住我",
+    resetPasswordSuccess: "重置密碼成功",
+    sendOTPSuccess: "發送驗證碼成功",
+    signIn: "登⼊",
+    signUpSuccess: "注冊成功!",
+    thanksRegisterWith: "感謝您注冊成爲會員",
+    tipNewUser: "新用戶?",
+    titleFleetRegistration: "車隊/司機用戶注冊"
+  },
+  home: {
+    all: "全部",
+    chargeable: "可充電",
+    chooseActivationType: "選擇激活類型",
+    chooseConnecterType: "選擇連結器類型",
+    chooseParkingFee: "選擇停車費",
+    chooseProvider: "選擇供應商",
+    done: "完成",
+    filterResults: "條結果",
+    filters: "站點過濾",
+    free: "免費",
+    later: "以後再說",
+    locationPermissionTips: "請授予位置權限以獲得更多服務",
+    myLocation: "位置",
+    newVersionName: "新版本:",
+    noSearch: "找不到站點",
+    search: "搜尋",
+    searchHint: "使用站點名稱或服務提供商搜尋",
+    statusPrivate: "私有站點",
+    updateNow: "現在更新",
+    versionUpdate: "發現新版本"
+  },
+  drawer: {
+    sign: "請登⼊",
+    charging: "充電",
+    wallet: "餘額",
+    topup: "餘額充值",
+    feedback: "反饋",
+    settings: "設定",
+    about: "關于",
+    logging: "登⼊中...",
+    privacyPolicy: "隱私權政策",
+    termsOfUse: "使用條款",
+    mapsTest: "地圖測試",
+    debugOnly: "以下爲調試模式獨有項",
+    noChargingSession: "您未在充電"
+  },
+  profile: {
+    address: "地區",
+    addVehicle: "添加車輛",
+    apply2Fleet: "注冊成爲車隊",
+    avatar: "大頭貼",
+    chooseConnecterType: "選擇充電器類型",
+    confirmDeleteAccount: "您確定要刪除您的帳戶嗎?此操作無法撤消",
+    deleteAccount: "刪除我的賬號",
+    deleteAccountSuccess: "刪除賬號成功!",
+    email: "信箱",
+    errPhoneNumberFormat: "電話號碼格式不正確",
+    hintPostal: "郵政編碼",
+    hintStreet: "街道名稱和編號",
+    hintUnitNo: "單元、門牌號",
+    labelCity: "城市",
+    licensePlate: "車牌號",
+    logout: "登出",
+    msgInputBrand: "請輸入車輛品牌",
+    msgInputModel: "請輸入車輛型號",
+    msgInputPlate: "請輸入車牌號",
+    myVehicles: "我的車輛:",
+    nickname: "名字",
+    notificationSettings: "通知設定",
+    noVehicles: "沒有車輛訊息",
+    phoneNumber: "電話號碼",
+    plsInputCity: "請輸入城市",
+    plsInputPostal: "請輸入郵政編碼",
+    plsInputStreet: "請輸入街道訊息",
+    plsInputUnitNo: "請輸入門牌號",
+    postalCode: "郵編",
+    registerDate: "註冊時間",
+    remove: "刪除車輛",
+    removeVehicle: "刪除車輛",
+    signOut: "登出",
+    street: "街道",
+    tipRemoveVehicle: "您確定要刪除此車輛嗎?",
+    tipSignOut: "您確定要登出嗎?",
+    unitNumber: "門牌號",
+    update: "更新",
+    updateNickname: "修改名字",
+    updatePhoneNumber: "修改電話號碼",
+    updateSuccess: "更新個人訊息成功",
+    userType: "賬號類別",
+    vehicleBrand: "車輛品牌",
+    vehicleModel: "車輛型號"
+  },
+  charging: {
+    AC: "AC",
+    DC: "DC",
+    Chademo: "CHAdeMO",
+    acChargers: "AC充電器",
+    additionalInfo: "附加訊息",
+    additionalInformation: "附加訊息",
+    btnAuthenticate: "驗證",
+    btnCancelReservation: "取消預訂",
+    btnComplete: "完成",
+    btnIntoCharging: "點擊進入充電",
+    btnOkay: "好的",
+    btnReserve: "預定",
+    btnStartCharging: "開始充電",
+    btnStopCharging: "停止充電",
+    cancleReservation: "取消預訂",
+    chargingInProgress: "正在充電中……",
+    chooseConnector: "選擇連結器",
+    chooseRate: "選擇費率",
+    comfirmCancleReservation: "您確定要取消預訂嗎?",
+    confirmStopCharging: "您確定要停止充電嗎?",
+    dcChargers: "DC充電器",
+    enterStationId: "輸入充電器ID",
+    errAuthenticationError: "似乎存在身份驗證錯誤!請重試",
+    errDetected: "檢測到錯誤,請重試",
+    errNotChargeE0: "您的車輛似乎沒有充電,請檢查您的車輛(E0)",
+    errNotConnected: "您的車輛未連結到充電樁,請檢查連結器",
+    errUnable2Charge: "充電樁無法爲您的車輛充電,請重新驗證",
+    errUnable2Reserved: "該充電樁已被預定,無法爲您的車輛充電",
+    free: "免費",
+    hintExampleStationId: "例:LEMOC0002-1",
+    labelAvailableTotal: "可使用/總計",
+    labelPower: "功率",
+    labelRate: "費率",
+    labelStatus: "狀態",
+    labelTimeElapsed: "充電用時",
+    labelTotalCharges: "總費用",
+    labelTotalkWh: "總電量(kWh)",
+    labelType: "類型",
+    lastUpdatedAt: "上次更新時間:",
+    noRates: "無費率訊息",
+    numberAvailable: "個可用",
+    operatingHours: "營運時間",
+    parkingFees: "停車費用",
+    plsInputStationId: "請輸入站點ID",
+    plsSelectConnnector: "請選擇連結器",
+    pressStartToBegin: "請按“開始充電”按鈕充電",
+    pullDownRefresh: "可下拉刷新",
+    ratesPrivateNote: "注意:此充電站僅供私人使用",
+    reservedSuccess: "預訂成功!",
+    reserveTimeLeft: "剩余時間",
+    scanQR: "掃描二維碼",
+    selectedCharger: "選擇的充電器",
+    paymentMethod: "選擇付款方式",
+    selectPaymentMethod: "選擇的付款方式",
+    siteAddress: "站點地址",
+    siteName: "站點名稱",
+    statusAuthenticated: "已驗證",
+    statusAvailable: "可使用",
+    statusCharging: "正在充電",
+    statusInitiating: "初始化中……",
+    statusNotConnected: "未連結",
+    statusPreparing: "准備中",
+    statusPrivate: "私有的",
+    statusUnavailable: "不可用",
+    statusInCharging: "正在充電",
+    stepAuthenticated: "已通過驗證",
+    stepAuthenticatedDesc: "請點擊開始充電",
+    stepAuthenticating: "驗證中",
+    stepAuthenticatingDesc: "我們正在驗證您的連接,請稍候",
+    stepChargingDesc: "您的車輛正在充電。\n點擊停止充電可結束充電",
+    stepInitializing: "初始化中",
+    stepInitializingDesc: "正在準備開始充電,請稍候……",
+    stepStoppingCharge: "正在停止充電",
+    stepStoppingChargeDesc: "正在停止充電,請稍候……",
+    stepInsertConnector: "等待插入連接器",
+    stepInsertConnectorDesc: "請將充電器插入車輛,然後點擊驗證按鈕",
+    tabCharge: "充電",
+    tabExplore: "探索",
+    tabInfo: "訊息",
+    tabReserve: "預訂",
+    tipsDisconnectConnector: "請斷開連結並將插頭放回充電樁",
+    tipsRatesTax: "所有費率均包括消費稅",
+    titleOops: "出錯啦!",
+    titleStopCharging: "停止充電",
+    toBeUpdated: "待更新",
+    unallowReservation: "此充電站暫不允許預訂",
+    unitHr: "時",
+    unitMin: "分"
+  },
+  wallet: {
+    atAglance: "本周總結",
+    averageCharge: "平均充電",
+    averageSpend: "平均支出",
+    averageTime: "平均時間",
+    balance: "餘額",
+    btnPurchase: "充值",
+    btnTop_Up: "充值",
+    creditIsBelow5: "您的餘額低于S$5.",
+    creditWallet: "餘額",
+    creditWalletLabel: "餘額:",
+    forWeekOf: "本周總結",
+    labelBreakdown: "充電訊息",
+    labelChargeDelivered: "交易電量:",
+    labelChargeRates: "充電費率(含消費稅):",
+    labelChargeTime: "充電時間:",
+    labelDateTime: "交易時間:",
+    labelExchangeRate: "匯率:",
+    labelFinalPayment: "實際支付:",
+    labelIdleFee: "空閑費用:",
+    labelPayment: "實付款(含消費稅):",
+    labelPaymentMadeBy: "付款方式:",
+    labelPreviousBalance: "扣款前餘額:",
+    labelReferenceId: "關聯編號:",
+    labelReservationFee: "預訂費用:",
+    labelResultingBalance: "當前餘額:",
+    labelStationId: "站點編號:",
+    labelSubtotal: "小計",
+    labelTransactionId: "交易編號:",
+    labelYourConnector: "連結器訊息",
+    labelYourStation: "站點訊息",
+    linkSubmitFeedback: "我要反饋",
+    noHistoryData: "沒有交易訊息",
+    perHrWeek: "小時/周",
+    perWeek: "周",
+    purchaseCredits: "充值",
+    statistics4HalfYear: "過去6個月的統計數據",
+    statistics4week: "本周統計數據",
+    tabHistory: "曆史交易",
+    tabOverview: "概述",
+    tipsLowCredits: "如果餘額不足,此交易將強行結束\n",
+    tipsReceipt: "充電收據將發送到您的電子信箱中",
+    titleChooseCreditValue: "選擇充值金額",
+    titleChoosePaymentType: "選擇付款方式",
+    titleLowCredits: "餘額過低",
+    topUp: "充值",
+    viewHistory: "曆史記錄"
+  },
+  payment: {
+    btnSave2gallery: "將二維碼儲存到相簿",
+    btnSetting: "去設定",
+    errInputCardNo: "請輸入正確的卡號",
+    errInputCvv: "請輸入正確的CVV",
+    errInputTill: "請輸入正確的有效期限",
+    errSave2gallery: "儲存到相簿失敗",
+    errSave2galleryPermission: "無法儲存二維碼,請授予存儲或相簿訪問權限。",
+    hintCardName: "持卡人的姓名",
+    hintValidTill: "月/年",
+    labelAmount: "金額:",
+    labelCardName: "卡片名稱",
+    labelCardNo: "卡號",
+    labelChooseAmount: "選擇金額:",
+    labelPayWith: "付款方式:",
+    labelTopUpAmount: "充值金額:",
+    labelValidTill: "有效期限",
+    openInBrowser: "在浏覽器中打開",
+    paymentCompleted: "付款完成",
+    paymentOption: "付款選項",
+    payPerUse: "按次付費",
+    plsInputCardName: "請輸入持卡人姓名",
+    plsInputCardNo: "請輸入卡號",
+    plsInputCvv: "請輸入CVV",
+    plsInputTill: "請輸入有效期限",
+    successSave2gallery: "儲存相簿成功",
+    tipsPayPerUse: "未使用的金額將被退還",
+    tipsPayPerUseCharge: "壹旦收到付款,將自動開始充電",
+    tipsPayPerUseSave: "請儲存二維碼,並使用您的銀行/電子錢包應用程式掃描二維碼",
+    tipsQrPayment: "付款完成後,充值金額將自動記入您的餘額",
+    tipsQrPaymentSave: "請儲存二維碼,並使用您的銀行應用程式掃描二維碼"
+  },
+  settings: {
+    autoRefreshInterval: "自動更新站點",
+    language: "語言",
+    maps: "地圖",
+    notification: "通知",
+    notifyChargingComplete: "充電完成時通知",
+    notifyLowBalance: "餘額低時通知",
+    notifyPromotionsOffers: "促銷和優惠訊息",
+    refreshInterval: "刷新時間",
+    seconds: "秒",
+    showMyLocations: "顯示當前位置"
+  },
+  feedback: {
+    errFeedbackType: "請選擇反饋類型",
+    errFeednackContent: "請鍵入反饋內容",
+    errFetchType: "無法獲取反饋類型",
+    labelConnector: "問題連結器",
+    labelContent: "反饋內容(500字內)",
+    labelStation: "問題充電樁",
+    labelUpload: "請上傳相關圖片",
+    searchingChargeBox: "搜尋充電樁",
+    selectConnector: "選擇連結器",
+    sendSuccess: "成功發送反饋!",
+    submitFeedback: "提交反饋",
+    tipsLetKnow: "請在下面告訴我們!",
+    tipsSomething: "有遇到什麽問題嗎?",
+    typeOfFeedback: "反饋類型"
+  }
+}

+ 417 - 0
Strides-APP/app/i18n/locales/zh.js

@@ -0,0 +1,417 @@
+/**
+ * 简体中文语言包
+ */
+export default {
+  name: "简体中文",
+  default: "跟随系统",
+  nav: {
+    ok: "确定",
+    no: " 否 ",
+    yes: " 是 ",
+    back: "返回",
+    view: "查看",
+    confirm: "确认",
+    cancel: "取消",
+    submit: "提交",
+    loading: "加载中...",
+    waiting: "请稍后..."
+  },
+  common: {
+    no: "否",
+    yes: "是",
+    back: "返回",
+    save: "保存",
+    error: "错误",
+    confirm: "确认",
+    cancel: "取消",
+    close: "关闭",
+    select: "请选择",
+    inputAddText: "请输入",
+    cropperTitle: "裁剪",
+    addFailed: "添加失败,请重试",
+    addSuccess: "添加成功",
+    updateFailed: "更新失败,请重试",
+    updateSuccess: "更新成功",
+    cancelFailed: "取消失败,请重试",
+    cancelSuccess: "取消成功",
+    uploadFailed: "上传失败,请重试",
+    uploadSuccess: "上传成功",
+    deleteFailed: "删除失败,请重试",
+    deleteSuccess: "删除成功",
+    submitSuccess: "提交成功",
+    pulldown2Refresh: "下拉刷新"
+  },
+  route: {
+    about: "关于",
+    addCards: "添加信用卡",
+    addVehicle: "添加车辆",
+    bookmarks: "我的收藏",
+    changePassword: "修改密码",
+    charging: "充电",
+    chargingSite: "充电站详情",
+    driverRegister: "车队注册",
+    editAddress: "编辑地址",
+    editProfile: "个人设置",
+    editVehicle: "修改车辆信息",
+    feedback: "反馈",
+    forgotPassword: "忘记密码",
+    makePayment: "收银台",
+    myVehicles: "我的车辆",
+    notificationTest: "Firebase通知测试",
+    paymentMethod: "支付方式",
+    paynow: "PAYNOW",
+    payPerUse: "按次付费",
+    privacyPolicy: "隐私政策",
+    profileSettings: "个人信息",
+    publicRegister: "公共用户注册",
+    qrScan: "扫一扫",
+    rating: "评价",
+    referral: "邀请",
+    search: "搜索",
+    settings: "设置",
+    summary: "交易详情",
+    termsOfUse: "使用条款",
+    topUp: "余额充值",
+    topUpWithCard: "使用信用卡充值",
+    wallet: "余额"
+  },
+  sign: {
+    aConfirmationEmailTo: "我们已向以下邮箱发送一封确认电子邮件",
+    agreePDVInfoAccurate: "我同意我提交的信息是最新且真实的",
+    back2Login: "回到登录",
+    btnLogin: "登录",
+    btnSendOTP: "获取验证码",
+    btnSignUp: "注册",
+    errContactNoFormat: "电话号码格式不正确",
+    errEmailFormat: "邮箱格式不正确",
+    errPasswordConfirm: "两此输入密码不一致",
+    errPasswordStrong: "密码强度不符合规则",
+    forgotPassword: "忘记密码",
+    hintPDVLicence: "PH/PDV驾照号码",
+    iHaveReadAndAgree: "我已阅读并同意《",
+    labelCompany: "公司",
+    labelConfirmPassword: "确认密码",
+    labelCountry: "国家",
+    labelCreatePassword: "创建密码",
+    labelDisplayName: "昵称",
+    labelE_mail: "邮箱",
+    labelEmail: "邮箱",
+    labelMobileNumber: "手机号码",
+    labelNewPassword: "新密码",
+    labelOtp: "验证码",
+    labelPassword: "密码",
+    labelPDVLicence: "PDV驾驶证号",
+    labelPDVPhotos: " 驾驶证照片\n(正面&背面)",
+    labelPhoneNumber: "手机号码",
+    labelValidateEmail: "邮箱验证码",
+    labelYourCompany: "你的公司",
+    linkAndLink: "》《",
+    linkAndLinkEnd: "》",
+    password8more: "8个以上字母",
+    passwordLeastNumber: "至少包含一个数字",
+    passwordLeastNumberAndSpecial: "至少包含一个数字和一个特殊字符",
+    passwordMustHave: "您的密码须包含以下字符:",
+    passwordStrength: "密码强度",
+    passwordUpperLower: "包含大写和小写字母",
+    plsInputContactNo: "请输入电话号码",
+    plsInputDiaplayName: "请输入您的昵称",
+    plsInputEmail: "请输入电子邮箱地址",
+    plsInputOTP: "请输入验证码",
+    plsInputPassword: "请输入密码",
+    plsInputPassword2: "请输入确认密码",
+    plsInputPDVLicence: "请输入PDV驾驶证号",
+    plsLoginTitle: "请登录",
+    plsUploadLicencePhotos: "请上传PDV驾驶证照片",
+    registerDriverUser: "注册成为为车队/PHV司机用户",
+    registerPublicUser: "注册为公共用户",
+    rememberMe: "记住我",
+    resetPasswordSuccess: "重置密码成功",
+    sendOTPSuccess: "发送验证码成功",
+    signIn: "登录",
+    signUpSuccess: "注册成功!",
+    thanksRegisterWith: "感谢您注册成为会员",
+    tipNewUser: "新用户?",
+    titleFleetRegistration: "车队/司机用户注册"
+  },
+  home: {
+    all: "全部",
+    chargeable: "可充电",
+    chooseActivationType: "选择激活类型",
+    chooseConnecterType: "选择连接器类型",
+    chooseParkingFee: "选择停车费",
+    chooseProvider: "选择供应商",
+    done: "完成",
+    filterResults: "条结果",
+    filters: "站点过滤",
+    free: "免费",
+    later: "以后再说",
+    locationPermissionTips: "请授予位置权限以获得更多服务",
+    myLocation: "定位",
+    newVersionName: "新版本:",
+    noSearch: "找不到站点",
+    search: "搜索",
+    searchHint: "使用站点名称或服务提供商搜索",
+    statusPrivate: "私有站点",
+    updateNow: "现在更新",
+    versionUpdate: "发现新版本"
+  },
+  drawer: {
+    sign: "请登录",
+    charging: "充电",
+    wallet: "余额",
+    topup: "余额充值",
+    feedback: "反馈",
+    settings: "设置",
+    about: "关于",
+    logging: "登录中...",
+    privacyPolicy: "隐私政策",
+    termsOfUse: "使用条款",
+    mapsTest: "地图测试",
+    debugOnly: "以下为调试模式独有项",
+    noChargingSession: "您未在充电"
+  },
+  profile: {
+    address: "地址",
+    addVehicle: "添加车辆",
+    apply2Fleet: "注册成为车队",
+    avatar: "头像",
+    chooseConnecterType: "选择充电器类型",
+    confirmDeleteAccount: "您确定要删除您的帐户吗?此操作无法撤消",
+    deleteAccount: "删除我的账号",
+    deleteAccountSuccess: "删除账号成功!",
+    email: "邮箱",
+    errPhoneNumberFormat: "电话号码格式不正确",
+    hintPostal: "邮政编码",
+    hintStreet: "街道名称和编号",
+    hintUnitNo: "单元、门牌号",
+    labelCity: "城市",
+    licensePlate: "车牌号",
+    logout: "注销",
+    msgInputBrand: "请输入车辆品牌",
+    msgInputModel: "请输入车辆型号",
+    msgInputPlate: "请输入车牌号",
+    myVehicles: "我的车辆:",
+    nickname: "昵称",
+    notificationSettings: "通知设置",
+    noVehicles: "没有车辆信息",
+    phoneNumber: "电话号码",
+    plsInputCity: "请输入城市",
+    plsInputPostal: "请输入邮政编码",
+    plsInputStreet: "请输入街道信息",
+    plsInputUnitNo: "请输入门牌号",
+    postalCode: "邮编",
+    registerDate: "注册时间",
+    remove: "删除车辆",
+    removeVehicle: "删除车辆",
+    signOut: "注销",
+    street: "街道",
+    tipRemoveVehicle: "您确定要删除此车辆吗?",
+    tipSignOut: "您确定要注销吗?",
+    unitNumber: "门牌号",
+    update: "更新",
+    updateNickname: "修改昵称",
+    updatePhoneNumber: "修改电话号码",
+    updateSuccess: "更新个人信息成功",
+    userType: "账户类型",
+    vehicleBrand: "车辆品牌",
+    vehicleModel: "车辆型号"
+  },
+  charging: {
+    AC: "AC交流",
+    DC: "DC直流",
+    Chademo: "CHAdeMO",
+    acChargers: "AC交流充电器",
+    additionalInfo: "附加信息",
+    additionalInformation: "附加信息",
+    btnAuthenticate: "验证",
+    btnCancelReservation: "取消预订",
+    btnComplete: "完成",
+    btnIntoCharging: "点此进入充电",
+    btnOkay: "好的",
+    btnReserve: "预定",
+    btnStartCharging: "开始充电",
+    btnStopCharging: "停止充电",
+    cancleReservation: "取消预订",
+    chargingInProgress: "正在充电中……",
+    chooseConnector: "选择连接器",
+    chooseRate: "选择费率",
+    comfirmCancleReservation: "您确定要取消预订吗?",
+    confirmStopCharging: "您确定要停止充电吗?",
+    dcChargers: "DC直流充电器",
+    enterStationId: "输入充电器ID",
+    errAuthenticationError: "似乎存在身份验证错误!请重试",
+    errDetected: "检测到错误,请重试",
+    errNotChargeE0: "您的车辆似乎没有充电,请检查您的车辆(E0)",
+    errNotConnected: "您的车辆未连接到充电桩,请检查连接器",
+    errUnable2Charge: "充电桩无法为您的车辆充电,请重新验证",
+    errUnable2Reserved: "该充电桩已被预定,无法为您的车辆充电",
+    free: "免费",
+    hintExampleStationId: "例:LEMOC0002-1",
+    labelAvailableTotal: "可使用/总计",
+    labelPower: "功率",
+    labelRate: "费率",
+    labelStatus: "状态",
+    labelTimeElapsed: "充电用时",
+    labelTotalCharges: "总费用",
+    labelTotalkWh: "总电量(kWh)",
+    labelType: "类型",
+    lastUpdatedAt: "上次更新时间:",
+    noRates: "无费率信息",
+    numberAvailable: "个可用",
+    operatingHours: "营运时间",
+    parkingFees: "停车费用",
+    plsInputStationId: "请输入站点ID",
+    plsSelectConnnector: "请选择连接器",
+    pressStartToBegin: "请按“开始充电”按钮充电",
+    pullDownRefresh: "可下拉刷新",
+    ratesPrivateNote: "注意:此充电站仅供私人使用",
+    reservedSuccess: "预订成功!",
+    reserveTimeLeft: "剩余时间",
+    scanQR: "扫描二维码",
+    selectedCharger: "选择的充电器",
+    paymentMethod: "选择支付方式",
+    selectPaymentMethod: "选择的付款方式",
+    siteAddress: "站点地址",
+    siteName: "站点名称",
+    statusAuthenticated: "已验证",
+    statusAvailable: "可使用",
+    statusCharging: "正在充电",
+    statusInitiating: "初始化中……",
+    statusNotConnected: "未连接",
+    statusPreparing: "准备中",
+    statusPrivate: "私有的",
+    statusUnavailable: "不可用",
+    statusInCharging: "正在充电",
+    stepAuthenticated: "已通过验证",
+    stepAuthenticatedDesc: "请点击开始充电",
+    stepAuthenticating: "验证中",
+    stepAuthenticatingDesc: "我们正在验证您的连接,请稍候",
+    stepChargingDesc: "您的车辆正在充电。\点击停止充电可结束充电",
+    stepInitializing: "初始化中",
+    stepInitializingDesc: "正在准备开始充电,请稍候……",
+    stepStoppingCharge: "正在停止充电",
+    stepStoppingChargeDesc: "正在停止充电,请稍候……",
+    stepInsertConnector: "等待插入连接器",
+    stepInsertConnectorDesc: "请将充电器插入车辆,然后点击验证按钮",
+    tabCharge: "充电",
+    tabExplore: "探索",
+    tabInfo: "信息",
+    tabReserve: "预订",
+    tipsDisconnectConnector: "请断开连接并将插头放回充电桩",
+    tipsRatesTax: "所有费率均包括消费税",
+    titleOops: "出错啦!",
+    titleStopCharging: "停止充电",
+    toBeUpdated: "待更新",
+    unallowReservation: "此充电站暂不允许预订",
+    unitHr: "时",
+    unitMin: "分"
+  },
+  wallet: {
+    atAglance: "本周总结",
+    averageCharge: "平均充电",
+    averageSpend: "平均支出",
+    averageTime: "平均时间",
+    balance: "余额",
+    btnPurchase: "充值",
+    btnTop_Up: "充值",
+    creditIsBelow5: "您的余额低于S$5.",
+    creditWallet: "余额",
+    creditWalletLabel: "余额:",
+    forWeekOf: "本周总结",
+    labelBreakdown: "充电信息",
+    labelChargeDelivered: "交易电量:",
+    labelChargeRates: "充电费率(含消费税):",
+    labelChargeTime: "充电时间:",
+    labelDateTime: "交易时间:",
+    labelExchangeRate: "汇率:",
+    labelFinalPayment: "最终支付:",
+    labelIdleFee: "空闲费用:",
+    labelPayment: "实付款(含消费税):",
+    labelPaymentMadeBy: "支付方式:",
+    labelPreviousBalance: "扣款前余额:",
+    labelReferenceId: "关联编号:",
+    labelReservationFee: "预订费用:",
+    labelResultingBalance: "当前余额:",
+    labelStationId: "站点编号:",
+    labelSubtotal: "小计",
+    labelTransactionId: "交易编号:",
+    labelYourConnector: "连接器信息",
+    labelYourStation: "站点信息",
+    linkSubmitFeedback: "我要反馈",
+    noHistoryData: "没有交易信息",
+    perHrWeek: "小时/周",
+    perWeek: "周",
+    purchaseCredits: "充值",
+    statistics4HalfYear: "过去6个月的统计数据",
+    statistics4week: "本周统计数据",
+    tabHistory: "历史交易",
+    tabOverview: "概述",
+    tipsLowCredits: "如果余额不足,此交易将强制结束\n",
+    tipsReceipt: "充电收据将发送到您的电子邮箱中",
+    titleChooseCreditValue: "选择充值金额",
+    titleChoosePaymentType: "选择支付方式",
+    titleLowCredits: "余额过低",
+    topUp: "充值",
+    viewHistory: "历史记录"
+  },
+  payment: {
+    btnSave2gallery: "将二维码保存到相册",
+    btnSetting: "去设置",
+    errInputCardNo: "请输入正确的卡号",
+    errInputCvv: "请输入正确的CVV",
+    errInputTill: "请输入正确的有效期限",
+    errSave2gallery: "保存到相册失败",
+    errSave2galleryPermission: "无法保存二维码,请授予存储或相册访问权限。",
+    hintCardName: "持卡人的姓名",
+    hintValidTill: "月/年",
+    labelAmount: "金额:",
+    labelCardName: "卡片名称",
+    labelCardNo: "卡号",
+    labelChooseAmount: "选择金额:",
+    labelPayWith: "付款方式:",
+    labelTopUpAmount: "充值金额:",
+    labelValidTill: "有效期限",
+    openInBrowser: "在浏览器中打开",
+    paymentCompleted: "付款完成",
+    paymentOption: "付款选项",
+    payPerUse: "按次付费",
+    plsInputCardName: "请输入持卡人姓名",
+    plsInputCardNo: "请输入卡号",
+    plsInputCvv: "请输入CVV",
+    plsInputTill: "请键入有效期限",
+    successSave2gallery: "保存相册成功",
+    tipsPayPerUse: "未使用的金额将被退还",
+    tipsPayPerUseCharge: "一旦收到付款,将自动开始充电",
+    tipsPayPerUseSave: "请保存二维码,并使用您的银行/电子钱包应用程序扫描二维码",
+    tipsQrPayment: "付款完成后,充值金额将自动记入您的余额",
+    tipsQrPaymentSave: "请保存二维码,并使用您的银行应用程序扫描二维码"
+  },
+  settings: {
+    autoRefreshInterval: "自动更新站点",
+    language: "语言",
+    maps: "地图",
+    notification: "通知",
+    notifyChargingComplete: "充电完成时通知",
+    notifyLowBalance: "余额低时通知",
+    notifyPromotionsOffers: "促销和优惠信息",
+    refreshInterval: "刷新时间",
+    seconds: "秒",
+    showMyLocations: "显示当前位置"
+  },
+  feedback: {
+    errFeedbackType: "请选择反馈类型",
+    errFeednackContent: "请键入反馈内容",
+    errFetchType: "无法获取反馈类型",
+    labelConnector: "问题连接器",
+    labelContent: "反馈内容(500字内)",
+    labelStation: "问题充电桩",
+    labelUpload: "请上传相关图片",
+    searchingChargeBox: "搜索充电桩",
+    selectConnector: "选择连接器",
+    sendSuccess: "成功发送反馈!",
+    submitFeedback: "提交反馈",
+    tipsLetKnow: "请在下面告诉我们!",
+    tipsSomething: "有遇到什么问题吗?",
+    typeOfFeedback: "反馈类型"
+  }
+}

BIN
Strides-APP/app/images/maps/ic_marker_star.png


BIN
Strides-APP/app/images/maps/ic_marker_unstar.png


BIN
Strides-APP/app/images/maps/ic_marker_upcoming.png


BIN
Strides-APP/app/images/site/charging-status-auth.png


BIN
Strides-APP/app/images/site/charging-status-charge.png


BIN
Strides-APP/app/images/site/charging-status-ready.png


BIN
Strides-APP/app/images/site/charging-status-unknow.png


+ 7 - 6
Strides-APP/app/pages/About.js

@@ -20,8 +20,8 @@ export default About = () => {
       <Text style={styles.versionName}>{app.versionName}</Text>
       <Text style={ui.flex1}></Text>
       <View style={ui.flexcc}>
-        <Text style={styles.linkText} onPress={() => startPage(PageList.condition)}>Terms of Use</Text>
-        <Text style={styles.linkText} onPress={() => startPage(PageList.privacy)}>Privacy Policy</Text>
+        <Text style={styles.linkText} onPress={() => startPage(PageList.condition)}>{$t("drawer.termsOfUse")}</Text>
+        <Text style={styles.linkText} onPress={() => startPage(PageList.privacy)}>{$t("drawer.privacyPolicy")}</Text>
       </View>
       <Text style={styles.copyright}>{'Copyright ' + /*app.versionName + ' Build ' + app.versionCode + */getYearRange()+' Strides YTL Pte. Ltd.'}</Text>
     </View>
@@ -41,12 +41,13 @@ const styles = StyleSheet.create({
   container: {
     flex: 1,
     alignItems: 'center',
-    backgroundColor: '#FFF'
+    backgroundColor: colorLight
   },
   logo: {
-    width: 238,
-    height: 75,
-    marginTop: $vw(25)
+    width: 215,
+    height: 70,
+    marginTop: $vw(25),
+    marginBottom: 12
   },
   appName: {
     color: '#000',

+ 71 - 16
Strides-APP/app/pages/Router.js

@@ -20,7 +20,7 @@ import RegisterDriver from './sign/RegisterDriver';
 import Home from './home/Drawer';
 import Search from './search/SearchV2';
 import ChargeDetails from './charge/Details';
-import QRScan from './chargeV2/QRScan';
+import QRScan from './charge/QRScan';
 import Feedback from './my/Feedback';
 import Privacy from './my/Privacy';
 import Profile from './my/ProfileV2';
@@ -49,7 +49,10 @@ import PayPerUse from './payment/PayPerUse';
 import PaymentWeb from './payment/PaymentWeb';
 import Settings from './Settings';
 import ChargeAdapter from './chargeV2/ChargeAdapter';
+import ChargingPage from './chargingV2/ChargingPage';
 import { BridgePage } from '../utils/routeUtil';
+import HeaderTitle from '../components/HeaderTitle';
+import Bookmarks from './bookmark/Bookmarks';
 
 export var PageList = {
   'splash': {
@@ -64,6 +67,7 @@ export var PageList = {
   },
   'search': {
     title: 'Search',
+    titleScope: 'route.search',
     component: Search
   },
   'login': {
@@ -71,90 +75,117 @@ export var PageList = {
   },
   'register': {
     component: RegisterV4,
-    title: 'Public Registration'
+    title: 'Public Registration',
+    titleScope: 'route.publicRegister'
   },
   'registerPublic': {
     component: RegisterPublic,
-    title: 'Public Registration'
+    title: 'Public Registration',
+    titleScope: 'route.publicRegister'
   },
   'registerFleet': {
     component: RegisterDriver,
-    title: 'Fleet / PHV Registration'
+    title: 'Fleet / PHV Registration',
+    titleScope: 'route.driverRegister'
   },
   'chargeDetail': {
     title: 'Charging Site',
+    titleScope: 'route.chargingSite',
     component: ChargeDetails
   },
   'chargeDetailPage': {
     title: 'Charging Site',
+    titleScope: 'route.chargingSite',
     component: ChargeAdapter
   },
+  'chargingPage': {
+    title: 'Charging',
+    titleScope: 'route.charging',
+    component: ChargingPage
+  },
   'scanqr': {
     title: 'QR Scan',
+    titleScope: 'route.qrScan',
     component: QRScan
   },
   'feedback': {
     title: 'Feedback',
+    titleScope: 'route.feedback',
     component: Feedback
   },
   'about': {
     title: 'About',
+    titleScope: 'route.about',
     component: About
   },
   'privacy': {
     title: 'Privacy Policy',
+    titleScope: 'route.privacyPolicy',
     component: Privacy
   },
-  'profile': {
-    title: 'Profile Settings',
-    component: Profile
-  },
   'condition': {
     title: 'Terms of Use',
+    titleScope: 'route.termsOfUse',
     component: Condition
   },
+  'profile': {
+    title: 'Profile Settings',
+    titleScope: 'route.profileSettings',
+    component: Profile
+  },
   'summary': {
     title: 'Summary',
+    titleScope: 'route.summary',
     component: Summary
   },
   'rating': {
     title: 'Your Rating',
+    titleScope: 'route.rating',
     component: Rating
   },
   'wallet': {
     title: 'Transactions',
+    titleScope: 'route.wallet',
     component: Wallet
   },
   'editProfile': {
     title: 'My Profile',
+    titleScope: 'route.editProfile',
     component: EditProfile
   },
   'editAddress': {
     title: 'Edit Address',
+    titleScope: 'route.editAddress',
     component: EditAddress
   },
   'referral': {
     title: 'Referral',
+    titleScope: 'route.referral',
     component: Referral
   },
   'topup': {
     title: 'Top Up',
+    titleScope: 'route.topUp',
     component: Topup
   },
   'topupV2': {
     title: 'Top Up',
+    titleScope: 'route.topUp',
     component: TopupV2
   },
   'topupNew': {
-    title: 'Purchase Credits',
+    title: 'Top Up',
+    titleScope: 'route.topUp',
     component: TopupNew
   },
   'addCard': {
     title: 'Add Cards',
+    titleScope: 'route.addCards',
     component: AddCard
   },
   'myVehicles': {
     title: 'My Vehicles',
+    titleScope: 'route.myVehicles',
     component: VehicleList,
     options: {
       headerRight: () => (
@@ -174,38 +205,47 @@ export var PageList = {
   },
   'addVehicle': {
     title: 'Add Vehicle',
+    titleScope: 'route.addVehicle',
     component: AddVehicle
   },
   'editVehicle': {
     title: 'Update Vehicle',
+    titleScope: 'route.editVehicle',
     component: EditVehicle
   },
   'paynow': {
     title: 'PAYNOW',
+    titleScope: 'route.paynow',
     component: PayNow
   },
   'paycard': {
     title: 'Top Up with Card',
+    titleScope: 'route.topUpWithCard',
     component: CreditCard
   },
   'formCard': {
     title: 'Top Up with Card',
+    titleScope: 'route.topUpWithCard',
     component: FormCard
   },
   'paymentMethod': {
     title: 'Payment Method',
+    titleScope: 'route.paymentMethod',
     component: PaymentMethod
   },
   'paymentWeb': {
     title: 'Make Payment',
+    titleScope: 'route.makePayment',
     component: PaymentWeb
   },
   'payPeruse': {
     title: 'Pay Per Use',
+    titleScope: 'route.payPerUse',
     component: PayPerUse
   },
   'notify': {
     title: 'Notification Test',
+    titleScope: 'route.notificationTest',
     component: Notify
   },
   'mapTest': {
@@ -213,14 +253,22 @@ export var PageList = {
   },
   'forgotPassword': {
     title: 'Forgot Password',
+    titleScope: 'route.forgotPassword',
     component: ResetPassword
   },
   'changePassword': {
     title: 'Account Security',
+    titleScope: 'route.changePassword',
     component: ResetPassword
   },
+  'bookmarks': {
+    title: "Bookmarks",
+    titleScope: 'route.bookmarks',
+    component: Bookmarks
+  },
   'settings': {
     title: 'Settings',
+    titleScope: 'route.settings',
     component: Settings
   }
 }
@@ -253,19 +301,26 @@ const noTitle = (opt = {}) => {
  * @param {Object} opt 标题栏选项
  * @returns 
  */
-const Title = (title, opt = {}) => {
-  return {
-    title: title,
+const Title = (title, opt = {}, titleScope) => {
+  const options = {
     headerShown: true,
     headerStyle: {
       ...titleHeight(),
       elevation: 0,
+      shadowOpacity: 0,
+      borderBottomWidth: 0,
       backgroundColor: colorLight //配置标题栏背景
     },
     headerTintColor: pageTitleTint, //配置标题栏文字和图标颜色
     headerBackTitle: ' ', //配置iOS返回按钮文字
     ...opt
   }
+  if (titleScope && $t) {
+    options.headerTitle = () => <HeaderTitle scope={titleScope}/>
+  } else {
+    options.title = title
+  }
+  return options;
 }
 
 
@@ -278,15 +333,15 @@ function getPages() {
   } else {
     PageList = bakPages;
   }
-  for (const page in PageList) {
-    var p = PageList[page]
-    keys[page] = page; 
+  for (const page in bakPages) {
+    var p = bakPages[page]
+    keys[page] = page;
     pages.push(
       <Stack.Screen
         key={page}
         name={page}
         component={p.component}
-        options={p.title ? Title(p.title, p.options) : noTitle(p.options)}
+        options={p.title ? Title(p.title, p.options, p.titleScope) : noTitle(p.options)}
       />
     );
   }

+ 103 - 42
Strides-APP/app/pages/Settings.js

@@ -3,11 +3,15 @@
  * @邠心vbe on 2022/12/9
  */
 import React, { Component } from 'react';
-import { View, Text, StyleSheet, Switch, Pressable } from 'react-native';
+import { View, Text, StyleSheet } from 'react-native';
 import apiUser from '../api/apiUser';
 import Button from '../components/Button';
 import Dropdown from '../components/Dropdown';
-import storage from '../utils/storage';
+import Switch from '../components/Switch';
+import { i18nUtil } from '../i18n';
+import routeUtil from '../utils/routeUtil';
+import storage, { getStorage, setStorage } from '../utils/storage';
+import app from '../../app.json';
 
 export const SETTINGS_KEY = 'CHARGE_SETTINGS'
 export const SettingUtil = {
@@ -37,15 +41,17 @@ export default class Settings extends Component {
     this.state = {
       settings: {
         alwaysLocation: true,
-        refreshInterval: 10
+        refreshInterval: undefined
       },
       userInfo: {
         notifyLowBalance: false,
         notifyChargingComplete: false,
         notifyPromotionsOffers: false
       },
+      localeList: [],
+      currentLocale: "",
       intervalList: [10, 15, 20, 30, 60, 90, 120],
-      switchStyle: isIOS ? { false: "#B2B2B2", true: colorAccent } : null,
+      //switchStyle: isIOS ? { false: "#B2B2B2", true: colorAccent } : null,
     };
     this.changed = false;
     //global.colorAccent = "#00A9FF"
@@ -64,6 +70,7 @@ export default class Settings extends Component {
         });
       }, true);
     });
+    this.getLocaleValues();
     SettingUtil.getSettings(res => {
       const info = this.state.settings;
       for (let item in info) {
@@ -77,6 +84,32 @@ export default class Settings extends Component {
     })
   }
 
+  getLocaleValues() {
+    const locales = [{
+      name: $t("default"),
+      value: ""//i18nUtil.getDefaultLocale()
+    }];
+    for (let item of i18nUtil.locales) {
+      if (item.enable) {
+        locales.push({
+          name: item.name,
+          value: item.value
+        })
+      }
+    }
+    this.setState({
+      localeList: locales
+    });
+    getStorage(i18nUtil.SETTING_KEY).then(res => {
+      if (res) {
+        this.setState({
+          currentLocale: res
+        })
+      }
+    })
+    console.log("getLocaleValues", locales);
+  }
+
   changeSwitch(key, value) {
     this.state.settings[key] = value
     //console.log('changeSwitch', item);
@@ -98,6 +131,7 @@ export default class Settings extends Component {
     this.setState({
       settings: this.state.settings
     })
+    console.log("设置数据", this.state.settings);
   }
 
   saveSettings() {
@@ -108,69 +142,93 @@ export default class Settings extends Component {
     }
   }
 
+  changeLocale(locale) {
+    if (locale !== this.state.currentLocale) {
+      /*this.setState({
+        currentLocale: locale
+      })*/
+      if (locale == "") {
+        i18nUtil.setlocale(i18nUtil.getDefaultLocale());
+      } else {
+        i18nUtil.setlocale(locale);
+      }
+      setStorage(i18nUtil.SETTING_KEY, locale);
+      routeUtil.resetToHome(this.props);
+    }
+  }
+
   render() {
     return (
       <View style={ui.flex1}>
-        <Text style={styles.title}>Notification</Text>
+        { app.modules.i18n &&
+          <View style={styles.menuView}>
+            <Text style={styles.buttonText}>{$t('settings.language')}</Text>
+            <Dropdown
+              list={this.state.localeList}
+              //title={$t('settings.language')}
+              nameKey="name"
+              valueKey="value"
+              value={this.state.currentLocale}
+              onChange={l => this.changeLocale(l)}
+              style={styles.localeSelect}
+              showIcon={false}
+              textStyle={styles.settingText}
+            />
+          </View>
+        }
+        <Text style={styles.title}>{$t('settings.notification')}</Text>
         <Button
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeNotifySwitch("notifyChargingComplete", !userInfo.notifyChargingComplete)}>
-          <Text style={styles.buttonText}>Notify me when charging complete</Text>
+          <Text style={styles.buttonText}>{$t('settings.notifyChargingComplete')}</Text>
           <Switch
             value={this.state.userInfo.notifyChargingComplete}
-            trackColor={this.state.switchStyle}
             onValueChange={v => this.changeNotifySwitch("notifyChargingComplete", v)}/>
         </Button>
         <Button
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeNotifySwitch("notifyLowBalance", !userInfo.notifyLowBalance)}>
-          <Text style={styles.buttonText}>Notify me when credits is low balance</Text>
+          <Text style={styles.buttonText}>{$t('settings.notifyLowBalance')}</Text>
           <Switch 
             value={this.state.userInfo.notifyLowBalance}
-            trackColor={isIOS ? { false: "#B2B2B2", true: colorAccent } : null}
             onValueChange={v => this.changeNotifySwitch("notifyLowBalance", v)}/>
         </Button>
         <Button
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeNotifySwitch("notifyPromotionsOffers", !userInfo.notifyPromotionsOffers)}>
-          <Text style={styles.buttonText}>Notify me for promotions and offers</Text>
+          <Text style={styles.buttonText}>{$t('settings.notifyPromotionsOffers')}</Text>
           <Switch
             value={this.state.userInfo.notifyPromotionsOffers}
-            trackColor={isIOS ? { false: "#B2B2B2", true: colorAccent } : null}
             onValueChange={v => this.changeNotifySwitch("notifyPromotionsOffers", v)}/>
         </Button>
-        <Text style={styles.title}>Maps</Text>
+        <Text style={styles.title}>{$t('settings.maps')}</Text>
         <Button
           style={styles.itemButton}
           viewStyle={styles.itemView}
           onClick={() => this.changeSwitch('alwaysLocation', !this.state.settings.alwaysLocation)}>
-          <Text style={styles.buttonText}>Always show my locations</Text>
+          <Text style={styles.buttonText}>{$t('settings.showMyLocations')}</Text>
           <Switch
             value={this.state.settings.alwaysLocation}
-            trackColor={this.state.switchStyle}
             onValueChange={v => this.changeSwitch('alwaysLocation', v)}/>
         </Button>
-        <View style={{height: 56}}>
-          <Pressable style={styles.dropdownView} android_ripple={ripple}>
-            <Text style={styles.buttonText}>Auto refresh interval</Text>
-            <Text style={styles.settingText}>{this.state.settings.refreshInterval}s</Text>
-          </Pressable>
+        <View style={styles.menuView}>
+          <Text style={styles.buttonText}>{$t('settings.autoRefreshInterval')}</Text>
           <Dropdown
-            style={styles.hideItem}
+            style={styles.localeSelect}
             list={this.state.intervalList}
-            title="Refresh interval"
-            suffixList="s"
-            showText={false}
+            title={$t('settings.refreshInterval')}
+            suffixList={$t('settings.seconds')}
+            suffixText={$t('settings.seconds')}
             showIcon={false}
+            autoSelect={false}
             value={this.state.settings.refreshInterval}
-            rippleStyle={ripple}
+            textStyle={styles.settingText}
             onChange={v => this.changeSettings("refreshInterval", v)}
           />
         </View>
-        
       </View>
     );
   }
@@ -179,7 +237,8 @@ export default class Settings extends Component {
 const styles = StyleSheet.create({
   title: {
     color: '#888',
-    fontSize: 12,
+    fontSize: 10,
+    marginTop: 1,
     ...$padding(8, 16, 4),
     backgroundColor: colorLight
   },
@@ -195,33 +254,35 @@ const styles = StyleSheet.create({
     alignItems: 'center',
     flexDirection: 'row'
   },
+  menuView: {
+    height: 56,
+    paddingLeft: 16,
+    paddingRight: 16,
+    overflow: 'hidden',
+    alignItems: 'center',
+    flexDirection: 'row',
+    backgroundColor: colorLight
+  },
   buttonText: {
     flex: 1,
     color: textPrimary,
     fontSize: 16
   },
   settingText: {
-    color: '#666',
-    fontSize: 15
+    color: '#888',
+    fontSize: 14,
+    textAlign: 'right'
   },
-  dropdownView: {
+  localeSelect: {
     top: 0,
     left: 0,
     right: 0,
-    height: 56,
-    paddingLeft: 16,
-    paddingRight: 16,
-    position: 'absolute',
-    alignItems: 'center',
-    flexDirection: 'row',
-    backgroundColor: colorLight
-  },
-  hideItem: {
-    flex: 1,
-    height: 56,
+    bottom: 0,
     paddingLeft: 16,
     paddingRight: 16,
-    alignItems: 'center',
-    flexDirection: 'row'
+    position: "absolute",
+    alignItems: "center",
+    flexDirection: "row",
+    justifyContent: 'flex-end'
   }
 })

+ 266 - 0
Strides-APP/app/pages/bookmark/Bookmarks.js

@@ -0,0 +1,266 @@
+/**
+ * 充电桩收藏页
+ * @邠心vbe on 2023/06/28
+ */
+import React, { Component } from 'react';
+import { View, Text, StyleSheet, TextInput, FlatList, Image, RefreshControl } from 'react-native';
+import apiStation from '../../api/apiStation';
+import Dialog from '../../components/Dialog';
+import { MyRefreshProps } from '../../components/ThemesConfig';
+import utils from '../../utils/utils';
+import { PageList } from '../Router';
+import ListViewV2 from '../search/ListViewV2';
+
+export default class Bookmarks extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      isSearch: false,
+      searchResult: [{id:0}],
+      latlng: {
+        lat: 0.00001,
+        lng: 0.00001
+      }
+    };
+  }
+
+  componentDidMount() {
+    this.getGeoLocation();
+  }
+
+  refreshList() {
+    this.setState({
+      isSearch: true
+    });
+    this.getGeoLocation();
+  }
+
+  getGeoLocation() {
+    navigator.geolocation.getCurrentPosition(location => {
+      let latlng = {
+        lat: location.coords.latitude,
+        lng: location.coords.longitude
+      }
+      this.setState({
+        latlng: latlng
+      })
+      this.searchStation(latlng);
+    }, error => {
+      console.info("[Search] getGeoLocation", error);
+      this.searchStation(this.state.latlng);
+    });
+  }
+
+  searchStation(latlng, time=500) {
+    this.setState({
+      isSearch: true
+    });
+    //latlng.siteName = this.searchWorld
+    apiStation.getBookmarkList(latlng).then(res => {
+      if (res.data.sites) {
+        const list = [];
+        res.data.sites.forEach(item => {
+          list.push({
+            id: item.sitePk,
+            name: item.siteName,
+            address: item.siteAddress,
+            latitude: item.locationLatitude,
+            longitude: item.locationLongitude,
+            acConnector: item.acConnector,
+            allConnector: item.allConnector,
+            dcConnector: item.dcConnector,
+            siteType: item.siteType,
+            favorite: item.favorite,
+            distance: utils.getDistance(item.distance),
+            serviceProvider: item.serviceProvider
+          });
+        });
+        //setTimeout(() => {
+          this.setState({
+            isSearch: false,
+            searchResult: list
+          });
+        //}, time);
+      }
+    }).catch(err => {
+      console.log('searchStation-err', err);
+      this.setState({
+        isSearch: false,
+        searchResult: []
+      });
+    });
+  }
+
+  favoriteSite(info) {
+    if (info?.id) {
+      Dialog.showProgressDialog();
+      apiStation.bookmarkSite(info.id).then(res => {
+        this.searchStation(this.state.latlng);
+      }).catch(err => {
+        toastShort(err);
+      }).finally(() => {
+        Dialog.dismissLoading();
+      })
+    }
+  }
+
+  intoStation(info) {
+    startPage(PageList.chargeDetailPage, {stationInfo: info, action: "bookmarks"});
+  }
+
+  listItem = (props) => {
+    return (
+      <ListViewV2 
+        {...props}
+        onPress={() => this.intoStation(props.item)}
+        onFavorite={() => this.favoriteSite(props.item)}/>
+    )
+  }
+
+  render() {
+    return (
+      <View style={styles.container}>
+        {/* <View style={styles.searchView}>
+          <Feather
+            name={'search'}
+            size={20}
+            color={'#999'}/>
+          <TextInput
+            style={styles.searchInput}
+            autoFocus={true}
+            maxLength={50}
+            numberOfLines={1}
+            returnKeyType={'search'}
+            clearButtonMode={'while-editing'}
+            placeholder={$t('home.searchHint')}
+            placeholderTextColor={textPlacehoder}
+            onChangeText={text => {
+              this.searchWorld = text;
+            }}
+            onSubmitEditing={() => {
+              //this.getGeoLocation();
+              this.searchStation(this.state.latlng, 1000);
+            }}/>
+        </View> */}
+        <FlatList
+          style={styles.listView}
+          data={this.state.searchResult}
+          renderItem={this.listItem}
+          keyExtractor={item => item.id}
+          keyboardShouldPersistTaps="always"
+          ListEmptyComponent={<Text style={styles.noResult}>{$t('home.noSearch')}</Text>}
+          refreshControl={
+            <RefreshControl
+              {...MyRefreshProps()}
+              refreshing={this.state.isSearch}
+              onRefresh={() => this.refreshList()}
+            />
+          }
+        />
+      </View>
+    );
+  }
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: pageBackground
+  },
+  searchView: {
+    marginTop: 16,
+    marginLeft: 16,
+    marginRight: 16,
+    marginBottom: 8,
+    paddingLeft: 16,
+    paddingRight: 16,
+    borderRadius: 60,
+    borderWidth: 1,
+    borderColor: '#E5E5E5',
+    alignItems: 'center',
+    flexDirection: 'row',
+    backgroundColor: '#F5F5F5'
+  },
+  searchInput: {
+    flex: 1,
+    color: textPrimary,
+    ...$padding(6, 8),
+    fontSize: 15,
+    marginLeft: 4,
+    lineHeight: 20
+  },
+  searchingView: {
+    padding: 16,
+    alignItems: 'center'
+  },
+  seachingIcon: {
+    width: 60,
+    height: 60
+  },
+  noResult: {
+    color: '#999',
+    fontSize: 14,
+    padding: 20,
+    textAlign: 'center',
+  },
+  listView: {
+    flex: 1
+  },
+  itemView: {
+    alignItems: 'center',
+    flexDirection: 'row',
+    borderBottomWidth: 1,
+    borderBottomColor: '#eee'
+  },
+  stationInfo: {
+    flex: 1,
+    padding: 16
+  },
+  nameView: {
+    paddingTop: 3,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  stationName: {
+    color: textPrimary,
+    fontSize: 18,
+    fontWeight: 'bold'
+  },
+  stationAddress: {
+    color: '#666',
+    fontSize: 14,
+    paddingBottom: 8
+  },
+  infoStatus: {
+    fontSize: 10,
+    paddingTop: 3,
+    paddingLeft: 8,
+    paddingRight: 8,
+    paddingBottom: 3,
+    borderRadius: 5,
+    marginLeft: 12,
+  },
+  selected: {
+    color: textPrimary,
+    backgroundColor: colorAccent
+  },
+  available: {
+    color: textLight,
+    backgroundColor: '#90DB0A'
+  },
+  unavailable: {
+    color: '#999',
+    fontSize: 10.5,
+    paddingTop: 7,
+    paddingLeft: 9,
+    paddingRight: 9,
+    paddingBottom: 7,
+    backgroundColor: '#CCC'
+  },
+  connectView: {
+    paddingTop: 4,
+    paddingBottom: 4,
+    alignItems: 'center',
+    flexDirection: 'row'
+  }
+});

+ 15 - 15
Strides-APP/app/pages/charge/Details.js

@@ -128,7 +128,7 @@ export default class Details extends Component {
             refreshId: this.state.refreshId + 1
           });
         }
-      }).catch(err => {
+      }).catch((err) => {
         toastLong(err);
         this.setState({
           refreshing: false
@@ -177,7 +177,7 @@ export default class Details extends Component {
         keyboardShouldPersistTaps={'handled'}
         refreshControl={
           <RefreshControl
-            {...MyRefreshProps}
+            {...MyRefreshProps()}
             refreshing={this.state.refreshing}
             onRefresh={() => this.onRefresh()}
           />
@@ -199,7 +199,7 @@ export default class Details extends Component {
                 numberOfLines={1}>{this.state.stationInfo?.name}</Text>
               {/* <Text style={styles.stationAddress}>{this.state.stationInfo?.address}</Text> */}
               <Provider
-                color='#333'
+                color={textPrimary}
                 providers={this.state.stationInfo?.serviceProvider}/>
             </View>
             <TouchableOpacity
@@ -224,7 +224,7 @@ export default class Details extends Component {
                 }}
                 source={require('../../images/charge/icon-type-stops.png')}/>
               <Text style={{
-                color: '#333',
+                color: textPrimary,
                 fontSize: 14
               }}>Available {this.getAvailable('box')}</Text>
             </View>
@@ -238,7 +238,7 @@ export default class Details extends Component {
                 }}
                 source={require('../../images/charge/icon-type-interfaces.png')}/>
               <Text style={{
-                color: '#333',
+                color: textPrimary,
                 fontSize: 14
               }}>Available {this.getAvailable('inc')}</Text>
             </View>
@@ -324,7 +324,7 @@ export default class Details extends Component {
 const styles = StyleSheet.create({
   container: {
     ...StyleSheet.absoluteFillObject,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   headerView: {
     paddingTop: 16,
@@ -343,13 +343,13 @@ const styles = StyleSheet.create({
     paddingRight: 16
   },
   stationName: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 20,
     fontWeight: 'bold',
     paddingBottom: 2
   },
   stationAddress: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 12,
   },
   directView: {
@@ -358,14 +358,14 @@ const styles = StyleSheet.create({
     alignItems: 'center',
   },
   lengthText: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 12,
     paddingTop: 2
   },
   chargingInfo: {
     zIndex: 2,
     alignItems: 'center',
-    borderColor: 'white',
+    borderColor: colorLight,
     borderTopWidth: 1,
     flexDirection: 'row'
   },
@@ -377,16 +377,16 @@ const styles = StyleSheet.create({
     justifyContent: 'center'
   },
   available2: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 14,
     borderLeftWidth: 1.5,
-    borderLeftColor: 'white',
+    borderLeftColor: colorLight,
   },
   tabContainer: {
     flex: 1,
     paddingTop: 14,
     marginTop: -32,
-    backgroundColor: 'white',
+    backgroundColor: colorLight,
     borderTopLeftRadius: 20,
     borderTopRightRadius: 20
   },
@@ -412,14 +412,14 @@ const styles = StyleSheet.create({
     flex: 1.2
   },
   tabText: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 15,
     marginLeft: -1,
     textAlign: 'center',
     borderLeftColor: '#eee'
   },
   active: {
-    color: '#fff',
+    color: textLight,
     backgroundColor: colorPrimary
   },
   tabContent: {

+ 49 - 27
Strides-APP/app/pages/charge/QRScan.js

@@ -1,12 +1,11 @@
 /**
- * 扫描二维码(废弃)
+ * 扫描二维码
  * @邠心vbe on 2021/03/24
  */
 import React, { Component } from 'react'
-import { Image, StyleSheet, Text, View } from 'react-native'
+import { StyleSheet, View } from 'react-native'
 import QRCodeScanner from 'react-native-qrcode-scanner';
 import { RNCamera } from 'react-native-camera';
-import { Styles } from '../../components/Toolbar';
 import apiCharge from '../../api/apiCharge';
 import { PageList } from '../Router';
 import Dialog from '../../components/Dialog';
@@ -54,13 +53,18 @@ export const QRResult = {
             QRResult.setResult(res.data);
             back(true)
           }
-        }).catch(err => {
+        }).catch(({err, code}) => {
           Dialog.dismissLoading();
           back(false, '')
           Dialog.showDialog({
             title: 'Error',
             message: err,
-            showCancel: false
+            showCancel: false,
+            callback: btn => {
+              if (code == 5194 && btn == Dialog.BUTTON_OK) {
+                startPage(PageList.myVehicles)
+              }
+            }
           });
         })
       } else {
@@ -84,15 +88,24 @@ export default class QRScan extends Component {
 
   componentDidMount() {
     //console.log(this.state.params);
-    setTimeout(() => {
-      this.setState({
-        isResult: false
-      });
-    }, 300);
     this.props.navigation.addListener('focus', () => {
-      this.setState({
-        isResult: false
-      });
+      setTimeout(() => {
+        this.setState({
+          isResult: false
+        });
+      }, 200);
+    });
+    this.props.navigation.addListener('beforeRemove', (e) => {
+      if (!this.state.isResult) {
+        e.preventDefault();
+        this.setState({
+          isResult: true
+        }, () => {
+          setTimeout(() => {
+            goBack();
+          }, 300);
+        });
+      }
     });
   }
 
@@ -131,20 +144,28 @@ export default class QRScan extends Component {
       if (res.data && res.data.chargeBoxId) {
         QRResult.setResult(res.data);
         if (res.data.sitePk) {
-          goBack();
-          startPage(PageList.chargeDetail, {stationInfo: {id: res.data.sitePk}, action: 'qr'});
+          if (this.state.params.actionDetail) {
+            startPage(PageList.chargeDetailPage, {stationInfo: {id: res.data.sitePk}, action: 'qr', from: PageList.home});
+          } else {
+            goBack();
+          }
+          //startPage(PageList.chargeDetail, {stationInfo: {id: res.data.sitePk}, action: 'qr'});
         }
       }
-    }).catch(err => {
+    }).catch(({err, code}) => {
       Dialog.showDialog({
         title: 'Error',
         message: err,
         showCancel: false,
-        callback: (e) => {
-        this.setState({
-          isResult: false
-        });
-      }});
+        callback: (btn) => {
+          this.setState({
+            isResult: false
+          });
+          if (code == 5194 && btn == Dialog.BUTTON_OK) {
+            startPage(PageList.myVehicles)
+          }
+        }
+      });
     })
   }
 
@@ -153,17 +174,18 @@ export default class QRScan extends Component {
       <View style={styles.container}>
         { !this.state.isResult
           ? <QRCodeScanner
-              fadeIn={false}
+              fadeIn={true}
               onRead={this.scanResult}
               reactivate={false}
               reactivateTimeout={1000}
-              cameraStyle={{ width: $width, height: $vht(100)}}
-              containerStyle={{ width: $width, height: $vht(100)}}
+              cameraStyle={{ width: $width, height: $vh(100)}}
+              containerStyle={{ width: $width, height: $vh(100)}}
               flashMode={RNCamera.Constants.FlashMode.off}
               checkAndroid6Permissions={true} />
-          : <Image
+          : <View style={ui.flex1}></View>
+          /*<Image
               style={Styles.logo}
-              source={require('../../images/tool-logo.png')}/>
+              source={require('../../images/app-logo.png')}/> */
         }
       </View>
     );
@@ -174,7 +196,7 @@ const styles = StyleSheet.create({
   container: {
     alignItems: 'center',
     justifyContent: 'center',
-    backgroundColor: '#242B32',
+    backgroundColor: '#000',
     ...StyleSheet.absoluteFillObject
   }
 })

+ 8 - 8
Strides-APP/app/pages/charge/Summary.js

@@ -7,10 +7,10 @@ import { View, Text, StyleSheet, Image, ScrollView, RefreshControl } from 'react
 import apiCharge from '../../api/apiCharge';
 import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
-import { MyRefreshProps } from '../../components/ThemesConfig';
 import utils from '../../utils/utils';
 import { PageList } from '../Router';
 import { ChargeStyle, TypeImage } from './Charging';
+import { MyRefreshProps } from '../../components/ThemesConfig';
 
 export default class Summary extends Component {
   constructor(props) {
@@ -70,7 +70,7 @@ export default class Summary extends Component {
           summaryInfo: res.data
         });
       }
-    }).catch(err => {
+    }).catch((err) => {
       Dialog.dismissLoading();
       toastShort(err);
       this.setState({
@@ -107,7 +107,7 @@ export default class Summary extends Component {
         style={styles.container}
         refreshControl={
           <RefreshControl
-            {...MyRefreshProps}
+            {...MyRefreshProps()}
             refreshing={this.state.refreshing}
             onRefresh={() => this.onRefresh()}
           />
@@ -229,7 +229,7 @@ const styles = StyleSheet.create({
     padding: 16,
     borderRadius: 10,
     marginBottom: 16,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   sections2: {
     paddingTop: 16,
@@ -238,7 +238,7 @@ const styles = StyleSheet.create({
     paddingBottom: 8,
     borderRadius: 10,
     marginBottom: 16,
-    backgroundColor: 'white'
+    backgroundColor: colorLight
   },
   formTitle: {
     color: '#000',
@@ -257,11 +257,11 @@ const styles = StyleSheet.create({
   },
   label: {
     flex: 1,
-    color: '#333',
+    color: textPrimary,
     fontSize: 13,
   },
   text: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 13,
   },
   stationInfoText: {
@@ -287,7 +287,7 @@ const styles = StyleSheet.create({
     marginBottom: 16
   },
   tipText: {
-    color: '#333',
+    color: textPrimary,
     fontSize: 12,
     textAlign: 'center',
     marginBottom: 8

+ 19 - 11
Strides-APP/app/pages/chargeV2/ChargeAdapter.js

@@ -34,16 +34,20 @@ export default class ChargeAdapter extends Component {
       stationInfo: {}
     }
     this.pageAdapter = [{
-      title: "Info",
+      title: $t('charging.tabInfo'),
+      name: "Info",
       component: TabInfos
     }, {
-      title: "Charge",
+      title: $t('charging.tabCharge'),
+      name: "Charge",
       component: Charge
     }/*, {
-      title: "Reserve",
+      title: $t('charging.tabReserve'),
+      name: "Reserve",
       component: Reserve
     }/*, {
-      title: "Explore",
+      title: $t('charging.tabExplore'),
+      name: "Explore",
       component: Explore
     }*/]
     this.tabBarStyle = {
@@ -52,7 +56,7 @@ export default class ChargeAdapter extends Component {
       scrollEnabled: this.pageAdapter.length > 3,
       indicatorStyle: styles.indicator,
       activeTintColor: textPrimary,
-      inactiveTintColor: textSecondary //"#E0E0E0", 
+      inactiveTintColor: textSecondary //"#E0E0E0",
     }
     this.action = "";
     this.isHide = false;
@@ -170,7 +174,7 @@ export default class ChargeAdapter extends Component {
         }, () => {
           this.setPageTitle();
         });
-        if (this.action == 'qr') {
+        if (!PagerUtil.ENABLE_NEW_UI && this.action == 'qr') {
           setTimeout(() => {
             this.changeTab(1)
           }, 300);
@@ -232,7 +236,8 @@ export default class ChargeAdapter extends Component {
   }
 
   changeTab(index) {
-    this.props.navigation.navigate(this.pageAdapter[index]?.title);
+    console.log("[changeTab]", index);
+    this.props.navigation.navigate(this.pageAdapter[index]?.name);
     /*this.setState({
       tabIndex: index
     })*/
@@ -246,7 +251,7 @@ export default class ChargeAdapter extends Component {
         keyboardShouldPersistTaps={'handled'}
         refreshControl={
           <RefreshControl
-            {...MyRefreshProps}
+            {...MyRefreshProps()}
             refreshing={this.state.refreshing}
             onRefresh={() => this.onPullRefresh()}
           />
@@ -255,13 +260,16 @@ export default class ChargeAdapter extends Component {
           style={{minHeight: $vht(100)}}
           tabBarOptions={this.tabBarStyle}
           lazy={false}
-          initialRouteName={PagerList.tabCharge}
-          lazyPreloadDistance={1}>
+          lazyPreloadDistance={1}
+          >
           { this.pageAdapter.map((item, index) => 
             <Tab.Screen
               key={index}
-              name={item.title}
+              name={item.name}
               component={item.component}
+              options={{
+                title: item.title
+              }}
             />
           )}
         </Tab.Navigator>

+ 42 - 8
Strides-APP/app/pages/chargeV2/PagerUtil.js

@@ -1,9 +1,14 @@
+import { getFocusedRouteNameFromRoute } from "@react-navigation/core";
 import { PagerList } from "./ChargeAdapter";
+import app from '../../../app.json';
 
 var chargeInfoState = global.chargeInfoState
 var refreshListener = [];
 
+const DEBUG = app.debug && !app.product;
+
 export default PagerUtil = {
+  ENABLE_NEW_UI: false, //是否启用新的充电页面
   getStationInfo: () => {
     return chargeInfoState.stationInfo ?? {}
   },
@@ -14,7 +19,9 @@ export default PagerUtil = {
     refreshListener.push(page)
   },
   setRefreshing: (route) => {
-    console.log("刷新子页面", route);
+    if (DEBUG) {
+      console.log("[PagerUtil]刷新指定子页面:", route);
+    }
     refreshListener.map((item, index) => {
       if (!route || route == item.props?.route?.name) {
         if (item.onRefresh)
@@ -28,17 +35,44 @@ export default PagerUtil = {
         item.onBackRefresh();
     })
   },
-  onCharge: () => {
-    startPage(PagerList.tabCharge);
+  onCharge: (props) => {
+    const isFocused = props?.navigation?.isFocused();
+    if (!isFocused) {
+      if (DEBUG) {
+        console.log("[PagerUtil]", "onCharge");
+      }
+      startPage(PagerList.tabCharge);
+    } else if (DEBUG) {
+      console.log("[PagerUtil]", "onCharge-skip");
+    }
   },
-  onReserve: () => {
-    startPage(PagerList.tabReserve);
+  onReserve: (props) => {
+    const isFocused = props?.navigation?.isFocused();
+    if (!isFocused) {
+      if (DEBUG) {
+        console.log("[PagerUtil]", "onReserve");
+      }
+      startPage(PagerList.tabReserve);
+    } else if (DEBUG) {
+      console.log("[PagerUtil]", "onReserve-skip");
+    }
   },
-  onEnterStation: () => {
-    startPage(PagerList.tabCharge);
+  onEnterStation: (props) => {
+    const isFocused = props?.navigation?.isFocused();
+    if (!isFocused) {
+      if (DEBUG) {
+        console.log("[PagerUtil]", "onEnterStation");
+      }
+      startPage(PagerList.tabCharge);
+    } else if (DEBUG) {
+      console.log("[PagerUtil]", "onEnterStation-skip");
+    }
   },
   onDestory: () => {
     chargeInfoState = {};
     refreshListener = [];
-  }
+  },
+  getCurrentRoute: (props) => (
+    getFocusedRouteNameFromRoute(props.route)
+  )
 }

+ 34 - 26
Strides-APP/app/pages/chargeV2/Summary.js

@@ -70,7 +70,7 @@ export default class Summary extends Component {
           summaryInfo: res.data
         });
       }
-    }).catch(err => {
+    }).catch((err) => {
       Dialog.dismissLoading();
       toastShort(err);
       this.setState({
@@ -109,7 +109,7 @@ export default class Summary extends Component {
         style={styles.container}
         refreshControl={
           <RefreshControl
-            {...MyRefreshProps}
+            {...MyRefreshProps()}
             refreshing={this.state.refreshing}
             onRefresh={() => this.onRefresh()}
           />
@@ -117,89 +117,97 @@ export default class Summary extends Component {
         <View style={{height:16}}></View>
         <View style={styles.sections}>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Transation ID:</Text>
+            <Text style={styles.label}>{$t('wallet.labelTransactionId')}</Text>
             <Text style={styles.text}>{this.state.summaryInfo.transactionPk}</Text>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Reference ID:</Text>
+            <Text style={styles.label}>{$t('wallet.labelReferenceId')}</Text>
             <Text style={styles.text}>{this.state.summaryInfo.chargingPk}</Text>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Date Time:</Text>
+            <Text style={styles.label}>{$t('wallet.labelDateTime')}</Text>
             <Text style={styles.text}>{this.state.summaryInfo.dateTime}</Text>
           </View>
         </View>
         <View style={styles.sections}>
-          <Text style={styles.formTitle}>Your Station</Text>
+          <Text style={styles.formTitle}>{$t('wallet.labelYourStation')}</Text>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Station ID: {this.state.summaryInfo.chargeBoxPk}</Text>
+            <Text style={styles.label}>{$t('wallet.labelStationId') + this.state.summaryInfo.chargeBoxPk}</Text>
           </View>
-          <Text style={styles.stationInfoText}>{this.state.summaryInfo.boxAddress}</Text>
+          <Text style={styles.stationInfoText}>{this.state.summaryInfo?.boxAddress}</Text>
         </View> 
 
         <View style={styles.sections2}>
-          <Text style={styles.formTitle}>Your Connector</Text>
+          <Text style={styles.formTitle}>{$t('wallet.labelYourConnector')}</Text>
           <View style={styles.stationInfoView}>
             <Image
               style={ChargeStyle.infoIcon}
               source={this.state.summaryInfo.connectorType?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
             <View style={ChargeStyle.infoGroup}>
               <Text style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorType}</Text>
-              <Text style={ChargeStyle.infoTitle}>Type</Text>
+              <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
             </View>
             <View style={ChargeStyle.infoGroup}>
               <Text style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorWattage}</Text>
-              <Text style={ChargeStyle.infoTitle}>Power</Text>
+              <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
             </View>
             <View style={ChargeStyle.infoGroup}>
               <Text style={ChargeStyle.infoText}>{this.state.summaryInfo.connectorRate}</Text>
-              <Text style={ChargeStyle.infoTitle}>Rates</Text>
+              <Text style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</Text>
             </View>
           </View>
         </View> 
 
         <View style={styles.sections}>
-          <Text style={styles.formTitle}>Breakdown</Text>
+          <Text style={styles.formTitle}>{$t('wallet.labelBreakdown')}</Text>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Reservation Fee:</Text>
+            <Text style={styles.label}>{$t('wallet.labelReservationFee')}</Text>
             <Text style={styles.text}>{currency}{this.state.summaryInfo.reservationFee ?? 0}</Text>
           </View>
           { utils.isNotEmpty(this.state.summaryInfo.idleFee) &&
             <View style={styles.formRow}>
-              <Text style={styles.label}>Idle Fee:</Text>
+              <Text style={styles.label}>{$t('wallet.labelIdleFee')}</Text>
               <Text style={styles.text}>{currency}{this.state.summaryInfo.idleFee}</Text>
             </View>
           }
           <View style={styles.formRow}>
-            <Text style={styles.label}>Charge Time:</Text>
+            <Text style={styles.label}>{$t('wallet.labelChargeTime')}</Text>
             <Text style={styles.text}>{utils.hour2HHmm(this.state.summaryInfo.chargeTime)}</Text>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Charge Delivered:</Text>
+            <Text style={styles.label}>{$t('wallet.labelChargeDelivered')}</Text>
             <Text style={styles.text}>{this.state.summaryInfo.chargeDelivered ?? 0}kWh</Text>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Charge Rates (GST Inclusive):</Text>
+            <Text style={styles.label}>{$t('wallet.labelChargeRates')}</Text>
             <Text style={styles.text}>{currency}{this.state.summaryInfo.chargeRates ?? '0.0'}</Text>
           </View>
         </View>
 
         <View style={styles.sections}>
-          <Text style={styles.formTitle}>Subtotal</Text>
+          <Text style={styles.formTitle}>{$t('wallet.labelSubtotal')}</Text>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Payment Made By:</Text>
+            <Text style={styles.label}>{$t('wallet.labelPaymentMadeBy')}</Text>
             <Text style={styles.text}>{this.state.summaryInfo.paymentType}</Text>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Previous Balance:</Text>
+            <Text style={styles.label}>{$t('wallet.labelPreviousBalance')}</Text>
             <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.previousBalance)}</Text>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Payment (GST Inclusive):</Text>
+            <Text style={styles.label}>{$t('wallet.labelPayment')}</Text>
             <Text style={styles.text}>{currency}{this.state.summaryInfo.payment ?? '0.0'}</Text>
           </View>
           <View style={styles.formRow}>
-            <Text style={styles.label}>Resulting Balance:</Text>
+            <Text style={styles.label}>{$t('wallet.labelExchangeRate')}</Text>
+            <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.exchangeRate)}</Text>
+          </View>
+          <View style={styles.formRow}>
+            <Text style={styles.label}>{$t('wallet.labelFinalPayment')}</Text>
+            <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.finalPayment)}</Text>
+          </View>
+          <View style={styles.formRow}>
+            <Text style={styles.label}>{$t('wallet.labelResultingBalance')}</Text>
             <Text style={styles.text}>{this.getSummaryText(this.state.summaryInfo.resultingBalance)}</Text>
           </View>
         </View>
@@ -207,10 +215,10 @@ export default class Summary extends Component {
           <View style={styles.bottomButton}>
             <Text
               style={styles.feedback}
-              onPress={() => startPage(PageList.feedback)}>Submit Your Feedback</Text>
-            <Text style={styles.tipText}>A Receipt Will Be Sent To Your Email</Text>
+              onPress={() => startPage(PageList.feedback)}>{$t('wallet.linkSubmitFeedback')}</Text>
+            <Text style={styles.tipText}>{$t('wallet.tipsReceipt')}</Text>
             <Button
-              text='Done'
+              text={$t('home.done')}
               elevation={1.5}
               onClick={() => this.toRating()}/>
           </View>

+ 245 - 498
Strides-APP/app/pages/chargeV2/TabCharge.js

@@ -3,19 +3,18 @@
  * @邠心vbe on 2023/02/06
  */
 import React, { Component } from 'react';
-import { View, Text, StyleSheet, ImageBackground, Image, ScrollView } from 'react-native';
+import { View, StyleSheet } from 'react-native';
 import apiCharge from '../../api/apiCharge';
-import Button from '../../components/Button';
 import Dialog from '../../components/Dialog';
 import { PageList } from '../Router';
-import { BatteryView, ChargeStyle, circleSize, DashboardView, EnterStationDialog, TypeImage } from './Charging';
-import Payment, { PaymentDefault, PAYTYPE } from '../wallet/Payment';
+import { EnterStationDialog } from './Charging';
 import { QRResult } from '../charge/QRScan';
 import { ErrorDialog } from './InfoDialog';
-import utils from '../../utils/utils';
 import PagerUtil from './PagerUtil';
-import { PaymentList } from './Payment';
-import BadgeSelectItem from '../../components/BadgeSelectItem';
+import StepStartView from '../charging/StepStartView';
+import StepChargeView from '../charging/StepChargeView';
+import utils from '../../utils/utils';
+import { PaymentDefault } from '../payment/PaymentConfig';
 
 export default class TabCharge extends Component {
   constructor(props) {
@@ -37,6 +36,7 @@ export default class TabCharge extends Component {
       showErrorDialog: false,
       showStationDialog: false,
       curerntPerUser: undefined,
+      canIntoCharging: false,
       //currentPayment: PAYTYPE.CREDIT_WALLET,
       //currentPaytype: "Credit Wallet",
       currentPayment: PaymentDefault.DEFAULT.payType,
@@ -45,12 +45,14 @@ export default class TabCharge extends Component {
     this.changeMethod = false;
     this.canAutoRefresh = false;
     this.inputStationId = '';
+    this.autoIntoCharging = true;
+    this.intoChargingStatus = ["Preparing", "Charging", "Initiating", "SuspendedEVSE", "SuspendedEV"]
   }
 
   componentDidMount() {
     this.canAutoRefresh = true;
     PagerUtil.addOnRefresh(this);
-    this.onRefresh();
+    //this.onRefresh();
   }
 
   onRefresh() {
@@ -60,7 +62,7 @@ export default class TabCharge extends Component {
       stationInfo: info
     }, () => {
       this.init();
-      //console.log("站点信息", this.state.stationInfo);
+      console.log("站点信息", this.state.stationInfo);
       //this.checkIsCharge();
     });
   }
@@ -73,12 +75,24 @@ export default class TabCharge extends Component {
       console.log("Charge刷新", "haveResult");
       const info = QRResult.getResult()
       console.log('QRResult', info);
-      this.setState({
-        isAuthentic: true,
-        connectorInfo: info
-        //soc: info.chargeType == 'AC' ? 0 : 'In Charging'
-      });
-      this.checkChargeStatus();
+      if (PagerUtil.ENABLE_NEW_UI) {
+        //新充电流程
+        this.setState({
+          connectorInfo: info
+        }, () => {
+          this.checkChargeStatus(true);
+        })
+      } else {
+        this.setState({
+          isAuthentic: true,
+          connectorInfo: info
+          //soc: info.chargeType == 'AC' ? 0 : 'In Charging'
+        }, () => {
+          PagerUtil.onCharge(this.props);
+          this.checkChargeStatus(true);
+        });
+      }
+      //QRResult.clearResult();
     } else if (this.state.isStart) {
       console.log("Charge刷新", "isStart");
       this.checkIsCharge();
@@ -109,6 +123,17 @@ export default class TabCharge extends Component {
     }
   }
 
+  toChargingPage() {
+    this.autoIntoCharging = false;
+    startPage(PageList.chargingPage, {
+      id: this.state.stationInfo.id,
+      name: this.state.stationInfo.name,
+      address: this.state.stationInfo.address,
+      chargeBoxId: this.state.connectorInfo.chargeBoxId,
+      connectorId: this.state.connectorInfo.connectorId
+    });
+  }
+
   enterStatioinId() {
     if (QRResult.haveResult()) {
       const info = QRResult.getResult()
@@ -148,300 +173,12 @@ export default class TabCharge extends Component {
     }
   }
 
-  //扫码之前和扫码之后的站点信息Section
-  StationInfo() {
-    return (
-      <BadgeSelectItem
-        style={ChargeStyle.stationInfoView}
-        checked={true}>
-        {/* <ImageBackground
-          style={{
-            width: 42,
-            height: 42
-          }}
-          source={require('../../images/charge/icon-station-no.png')}>
-          <Text style={{
-            left: 0,
-            right: 0,
-            bottom: 1,
-            fontSize: 8,
-            color: 'white',
-            textAlign: 'center',
-            position: 'absolute'
-          }}>{this.state.connectorInfo.connectorId}</Text>
-        </ImageBackground> */}
-        <Image
-          style={ChargeStyle.infoIcon}
-          source={this.state.connectorInfo.chargeType.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
-        <View style={ChargeStyle.infoGroup}>
-          <Text
-            numberOfLines={1}
-            ellipsizeMode="tail"
-            style={ChargeStyle.infoText}>{this.state.connectorInfo.chargeType}{this.state.connectorInfo.wattage}</Text>
-          <Text style={ChargeStyle.infoTitle}>Type</Text>
-        </View>
-        <View style={ChargeStyle.infoGroup}>
-          <Text
-            numberOfLines={1}
-            ellipsizeMode="tail"
-            style={ChargeStyle.infoText}>{currency}{this.state.connectorInfo.rate}/{this.state.connectorInfo.rateType}</Text>
-          <Text style={ChargeStyle.infoTitle}>Rate</Text>
-        </View>
-        <View style={ChargeStyle.infoGroup}>
-          <Text
-            numberOfLines={1}
-            ellipsizeMode="tail"
-            style={ChargeStyle.infoText}>{this.state.connectorInfo.wattage}kW{/*this.state.connectorInfo.rateType*/}</Text>
-          <Text style={ChargeStyle.infoTitle}>Power</Text>
-        </View>
-        <View style={ChargeStyle.infoGroup}>
-          { this.state.isCharging
-          ? (this.state.isPending
-            ? (
-                <Text
-                  numberOfLines={1}
-                  ellipsizeMode="tail"
-                  style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
-                  Preparing
-                </Text>
-              )
-            : (
-                <Text
-                  numberOfLines={1}
-                  ellipsizeMode="tail"
-                  style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
-                  Charging
-                </Text>
-              )
-            )
-          : (this.state.connectorInfo.isCheckThrough
-            ? (
-                <Text
-                  numberOfLines={1}
-                  ellipsizeMode="tail"
-                  style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
-                  Authenticated
-                </Text>
-              )
-            : (
-                <Text
-                  numberOfLines={1}
-                  ellipsizeMode="tail"
-                  style={[ChargeStyle.infoStatus, ChargeStyle.statusError]}>
-                  Not Connected
-                </Text>
-              )
-            )
-          }
-          <Text style={ChargeStyle.infoTitle}>Status</Text>
-        </View>
-      </BadgeSelectItem>
-    );
-  }
-  //扫码之前-站点信息Section-end
-
-  //初始页面-扫码认证之前
-  StepRateView() {
-    return (
-      <View>
-        <View style={{minHeight: $vht(80)}}>
-          <Text style={styles.gstText}>All rates Include 8% GST</Text>
-          <Text style={styles.title}>AC Chargers ({this.state.stationInfo?.acConnector?.available ?? "0"} available)</Text>
-          { this.state.stationInfo.acRates?.length > 0
-            ? this.state.stationInfo.acRates.map((item, index) => {
-                return (
-                  <View key={index} style={ChargeStyle.stationInfoView}>
-                    <Image
-                      style={ChargeStyle.infoIcon}
-                      source={TypeImage.AC}/>
-                    <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoText}>{item.type}</Text>
-                      <Text style={ChargeStyle.infoTitle}>Type</Text>
-                    </View>
-                    <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoText}>{item.rates}</Text>
-                      <Text style={ChargeStyle.infoTitle}>Rate</Text>
-                    </View>
-                    <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoText}>{item.power}</Text>
-                      <Text style={ChargeStyle.infoTitle}>Power</Text>
-                    </View>
-                    <View style={ChargeStyle.infoGroup}>
-                      { item?.connectorCount?.available > 0
-                        ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</Text>
-                        : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</Text>
-                      }
-                      <Text style={ChargeStyle.infoTitle}>Status</Text>
-                    </View>
-                  </View>
-                );
-              })
-            : <Text style={ui.noData}>No Rates</Text>
-          }
-          <Text style={styles.title}>DC Chargers ({this.state.stationInfo?.dcConnector?.available ?? "0"} available)</Text>
-          { this.state.stationInfo.dcRates?.length > 0
-            ? this.state.stationInfo.dcRates.map((item, index) => {
-                return (
-                  <View key={index} style={ChargeStyle.stationInfoView}>
-                    <Image
-                      style={ChargeStyle.infoIcon}
-                      source={TypeImage.DC}/>
-                    <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoText}>{item.type}</Text>
-                      <Text style={ChargeStyle.infoTitle}>Type</Text>
-                    </View>
-                    <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoText}>{item.rates}</Text>
-                      <Text style={ChargeStyle.infoTitle}>Rate</Text>
-                    </View>
-                    <View style={ChargeStyle.infoGroup}>
-                      <Text style={ChargeStyle.infoText}>{item.power}</Text>
-                      <Text style={ChargeStyle.infoTitle}>Power</Text>
-                    </View>
-                    <View style={ChargeStyle.infoGroup}>
-                      { item?.connectorCount?.available > 0
-                        ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</Text>
-                        : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</Text>
-                      }
-                      <Text style={ChargeStyle.infoTitle}>Status</Text>
-                    </View>
-                  </View>
-                );
-              })
-            : <Text style={ui.noData}>No Rates</Text>
-          }
-          { this.state.isPrivate &&
-            <View style={styles.privateView}> 
-              <Text style={styles.privateText}>NOTE: The charging stations are for private usage.</Text>
-            </View>
-          }
-        </View>
-        {/* <Payment refreshId={this.state.refreshId}/> */}
-        <View style={styles.buttonGroup}>
-          <Button
-            style={styles.buttonLeft}
-            text='Scan QR'
-            //disabled={this.state.available}
-            onClick={() => {
-              startPage(PageList.scanqr, {actionDetail: false, id: this.state.stationInfo.id});
-            }}/>
-          <Button
-            style={styles.buttonRight}
-            text='Enter Station ID'
-            //disabled={this.state.available}
-            onClick={() => {
-              this.setState({
-                showStationDialog: true
-              })
-            }}/>
-        </View>
-      </View>
-    );
-  }
-  //初始页面-扫码认证之前-end
-
-  //扫码认证之后-充电开始之前
-  StepStartView() {
-    return (
-      <>
-        <View style={{minHeight: $vht(80)}}>
-          <DashboardView isCharging={this.state.isCharging}/>
-          <Text style={styles.title}>Selected Charger</Text>
-          {this.StationInfo()}
-          {/* <View style={ui.center}>
-            <ImageBackground
-              style={styles.batteryBorder}
-              source={require('../../images/charge/ic-charge-circle.png')}>
-              <Text style={{
-                color: textPrimary,
-                fontSize: 16,
-                lineHeight: 22,
-                textAlign: 'center'
-              }}>
-                Press<Text style={{padding: 10, fontWeight: 'bold'}}> Start </Text>to begin Charging
-              </Text>
-            </ImageBackground>
-          </View> */}
-          <Text style={styles.title}>Select Payment Method</Text>
-          <PaymentList
-            payType={this.state.currentPayment}
-            payPerUse={this.state.curerntPerUser}
-            onMethodChange={(type) => this.onPaymentMethodChanged(type)}
-          />
-          {/* <Payment 
-            refreshId={this.state.refreshId}
-            payType={this.state.currentPaytype}
-            balance={this.state.curerntPerUser}
-            isPayPerUse={this.state.currentPayment == PAYTYPE.PAY_PER_USE}
-            onMethodChange={() => this.onMethodChange()}
-          /> */}
-        </View>
-        <Button
-          style={styles.buttonView}
-          text='START CHARGING'
-          elevation={1.5}
-          onClick={() => this.startCharge()}/>
-      </>
-    );
-  }
-  //扫码认证之后-充电开始之前-end
-
-  //正在充电页面
-  StepChargeView() {
-    return (
-      <>
-        <View style={{minHeight: $vht(80)}}>
-          <DashboardView isCharging={this.state.isCharging} connectorInfo={this.state.connectorInfo}/>
-          <Text style={styles.title}>Selected Charger</Text>
-          {this.StationInfo()}
-
-          {/* <BatteryView
-            soc={this.state.connectorInfo.batteryPercent}
-            isCharging={this.state.isCharging}
-            isPending={this.state.isPending}
-          /> */}
-          
-          <Text style={styles.title}>Select Payment Method</Text>
-          <PaymentList
-            isSelect={false}
-            payType={this.state.currentPayment}
-            payPerUse={this.state.curerntPerUser}
-            onMethodChange={(type) => this.onPaymentMethodChanged(type)}
-          />
-          {/* <Payment 
-            refreshId={this.state.refreshId}
-            payType={this.state.currentPaytype}
-            balance={this.state.curerntPerUser}
-          /> */}
-          { this.state.lastUpdated
-            ? <Text style={styles.updateTip}>{'Last updated at ' + this.state.lastUpdated + '\nPull down to refresh'}</Text>
-            : null
-          }
-        </View>
-        <Button
-          style={styles.buttonView}
-          disabled={this.state.isPending}
-          text={this.state.isCharging ? 'STOP CHARGING' : 'Complete'}
-          elevation={1.5}
-          onClick={() => {
-            if (this.state.isCharging) {
-              Dialog.showDialog({
-                title: 'Stop Charging',
-                message: 'Are you sure stop charging?',
-                callback: ok => {
-                  if (ok == Dialog.BUTTON_OK) {
-                    this.stopCharge();
-                  }
-                }
-              });
-            } else {
-              this.stopCharge();
-            }
-          }}/>
-      </>
-    );
+  //打开输入弹窗
+  onEnterStation() {
+    this.setState({
+      showStationDialog: true
+    })
   }
-  //正在充电页面-end
 
   //自动刷新
   autoCheckIsCharge() {
@@ -459,20 +196,21 @@ export default class TabCharge extends Component {
     }, 10000);
   }
 
+  //扫码后获取连接器数据
   getConnectorInfo() {
-    console.log("getConnectorInfo", this.state.stationInfo.id);
+    console.log("[TabCharge] getConnectorInfo", this.state.stationInfo.id);
     apiCharge.checkIsCharging({sitePk: this.state.stationInfo.id}).then(res => {
       this.setState({
         connectorInfo: res.data
       }, () => {
         this.checkChargeStatus();
       });
-    }).catch(err => {
+    }).catch((err) => {
       
     });
   }
 
-  //获取充电数据(百分比)
+  //获取充电进度数据(百分比)
   checkIsCharge(showError) {
     const params = {
       sitePk: this.state.stationInfo.id
@@ -490,15 +228,17 @@ export default class TabCharge extends Component {
           this.autoCheckIsCharge();
         }, 30000);
       }
-      PagerUtil.onCharge();
-    }).catch(err => {
+      PagerUtil.onCharge(this.props);
+    }).catch(({err, code}) => {
       //TODO 模拟测试
+      //TODO 模拟测试-End
       this.setState({
         isStart: false,
         isCharging: false
       });
       setTimeout(() => {
         this.autoCheckIsCharge();
+        this.canAutoRefresh = false;
       }, 30000);
       if (showError) {
         this.setState({
@@ -509,9 +249,10 @@ export default class TabCharge extends Component {
       }
     });
   }
+  //获取充电进度数据-end
 
   //获取充电桩对应接口的状态
-  checkChargeStatus() {
+  checkChargeStatus(haveResult=false) {
     //TODO 模拟测试
     /*this.setState({
       isStart: true,
@@ -519,125 +260,151 @@ export default class TabCharge extends Component {
       isAuthentic: true
     });
     return;*/
+    //TODO 模拟测试-End
     const params = {
       connectorId: this.state.connectorInfo.connectorId,
       chargeBoxId: this.state.connectorInfo.chargeBoxId,
     }
-    if (!params.chargeBoxId || !params.connectorId) {
-      this.checkIsCharge();
-      return;
+    if (!PagerUtil.ENABLE_NEW_UI) {
+      if (!params.chargeBoxId || !params.connectorId) {
+        this.checkIsCharge();
+        return;
+      }
     }
     apiCharge.getCurrentStatus(params).then(res => {
       if (res.data.status) {
-        switch (res.data.status) {
-          case 'Available': //可用的
-            this.state.connectorInfo.isCheckThrough = false;
-            this.setState({
-              isStart: false,
-              isPending: false,
-              isCharging: false,
-              connectorInfo: this.state.connectorInfo
-            });
-            break;
-          case 'Preparing': //已插入
-            this.state.connectorInfo.isCheckThrough = true;
-            this.setState({
-              isStart: false,
-              isPending: false,
-              isCharging: false,
-              available: true,
-              connectorInfo: this.state.connectorInfo
-            });
-            //this.checkIsCharge();
-            break;
-          case 'Charging': //正在充电
-            this.canAutoRefresh = true;
-            this.state.connectorInfo.isCheckThrough = true;
-            this.setState({
-              isPending: false,
-              connectorInfo: this.state.connectorInfo
-            });
-            this.checkIsCharge();
-            break;
-          case 'Initiating': //充电确认中
-            this.canAutoRefresh = true;
-            this.state.connectorInfo.isCheckThrough = true;
-            this.setState({
-              isStart: true,
-              isPending: true,
-              isCharging: true,
-              isAuthentic: true,
-              connectorInfo: this.state.connectorInfo
-            });
-            this.autoCheckChargeStatus();
-            break;
-          case 'SuspendedEVSE':
-            this.setState({
-              errorCode: 'A5',
-              showErrorDialog: true,
-              errorMessage: 'The charging station is unable to charge your vehicle.Please reauthenticate.'
-            });
-            break;
-          case 'SuspendedEV': //已连接上但未充电
-            this.checkIsCharge(true);
-            break;
-          case 'Reserved': //预定中
-            this.setState({
-              errorCode: 'A5',
-              showErrorDialog: true,
-              errorMessage: 'The charging station is reserved and unable to charge your vehicle.'
-            });
-            break;
-          case 'Finishing': //已完成
-            if (res.data.chargingPk) {
-              Dialog.showProgressDialog();
-              setTimeout(() => {
-                Dialog.dismissLoading();
+        if (PagerUtil.ENABLE_NEW_UI && (this.intoChargingStatus.indexOf(res.data.status) >= 0 || haveResult)) {
+          //进入新流程
+          this.setState({
+            canIntoCharging: true
+          }, () => {
+            if (this.autoIntoCharging) {
+              this.toChargingPage();
+            } else {
+              PagerUtil.onCharge(this.props);
+            }
+          })
+        } else {
+          if (haveResult) {
+            PagerUtil.onCharge(this.props);
+          }
+          switch (res.data.status) {
+            case 'Available': //可用的
+              this.state.connectorInfo.isCheckThrough = false;
+              this.setState({
+                isStart: false,
+                isPending: false,
+                isCharging: false,
+                connectorInfo: this.state.connectorInfo
+              });
+              break;
+            case 'Preparing': //已插入
+              this.state.connectorInfo.isCheckThrough = true;
+              this.setState({
+                isStart: false,
+                isPending: false,
+                isCharging: false,
+                available: true,
+                connectorInfo: this.state.connectorInfo
+              });
+              //this.checkIsCharge();
+              break;
+            case 'Charging': //正在充电
+              this.canAutoRefresh = true;
+              this.state.connectorInfo.isCheckThrough = true;
+              this.setState({
+                isPending: false,
+                connectorInfo: this.state.connectorInfo
+              });
+              this.checkIsCharge();
+              break;
+            case 'Initiating': //充电确认中
+              this.canAutoRefresh = true;
+              this.state.connectorInfo.isCheckThrough = true;
+              this.setState({
+                isStart: true,
+                isPending: true,
+                isCharging: true,
+                isAuthentic: true,
+                connectorInfo: this.state.connectorInfo
+              });
+              this.autoCheckChargeStatus();
+              break;
+            case 'SuspendedEVSE':
+              this.setState({
+                errorCode: 'A5',
+                showErrorDialog: true,
+                errorMessage: $t('charging.errUnable2Charge')
+              });
+              break;
+            case 'SuspendedEV': //已连接上但未充电
+              this.checkIsCharge(true);
+              break;
+            case 'Reserved': //预定中
+              this.setState({
+                errorCode: 'A5',
+                showErrorDialog: true,
+                errorMessage: $t('charging.errUnable2Reserved')
+              });
+              break;
+            case 'Finishing': //已完成
+              if (res.data.chargingPk) {
+                Dialog.showProgressDialog();
+                setTimeout(() => {
+                  Dialog.dismissLoading();
+                  this.setState({
+                    isStart: false,
+                    isPending: false,
+                    isCharging: false
+                  });
+                  startPage(PageList.summary, { 
+                    chargingPk: res.data.chargingPk,
+                    id: this.state.stationInfo.id,
+                    name: this.state.stationInfo.name,
+                    address: this.state.stationInfo.address
+                  });
+                }, 2000);
+              } else {
                 this.setState({
-                  isStart: false,
+                  isStart: true,
                   isPending: false,
                   isCharging: false
                 });
-                startPage(PageList.summary, { 
-                  chargingPk: res.data.chargingPk,
-                  id: this.state.stationInfo.id,
-                  name: this.state.stationInfo.name,
-                  address: this.state.stationInfo.address
-                });
-              }, 2000);
-            } else {
+              }
+              break;
+            default:
               this.setState({
-                isStart: true,
+                isStart: false,
                 isPending: false,
                 isCharging: false
               });
-            }
-            break;
-          default:
-            this.setState({
-              isStart: false,
-              isPending: false,
-              isCharging: false
-            });
-            this.setState({
-              errorCode: 'A4',
-              showErrorDialog: true,
-              errorMessage: 'Your vehicle doesn’t seem to be charging. Please check your vehicle. (E0)'
-            });
-            break;
+              this.setState({
+                errorCode: 'A4',
+                showErrorDialog: true,
+                errorMessage: $t('charging.errNotChargeE0')
+              });
+              break;
+          }
         }
+      } else {
+        this.setState({
+          errorCode: 'A4',
+          showErrorDialog: true,
+          errorMessage: $t('charging.errNotChargeE0')
+        });
       }
-    }).catch(err => {
+    }).catch((err) => {
       toastShort(err)
       this.setState({
         errorCode: 'A9',
         showErrorDialog: true,
-        errorMessage: 'There seems to be an authentication error! Please try again'
+        errorMessage: $t('charging.errAuthenticationError')
       });
     })
   }
+  //获取充电桩对应接口的状态-end
 
-  //开始充电api
+  //开始充电-start
   startCharge() {
     if (this.state.connectorInfo.isCheckThrough) {
       Dialog.showProgressDialog();
@@ -646,39 +413,66 @@ export default class TabCharge extends Component {
         connectorId: this.state.connectorInfo.connectorId
       }
       apiCharge.startCharge(params).then(res => {
-        this.setState({
-          isStart: true,
-          isPending: true,
-          isCharging: true
-        });
-        if (res.msg) {
-          toastShort(res.msg)
-        }
-        this.canAutoRefresh = true;
-        this.autoCheckChargeStatus();
-        /*setTimeout(() => {
-          this.autoCheckIsCharge();
-        }, 30000);*/
-        Dialog.dismissLoading();
-      }).catch(err => {
+        console.log(res);
+        setTimeout(() => {
+          this.setState({
+            isStart: true,
+            isPending: true,
+            isCharging: true
+          });
+          this.canAutoRefresh = true;
+          this.autoCheckChargeStatus();
+          Dialog.dismissLoading();
+          if (res.msg) {
+            Dialog.showResultDialog(res.msg)
+          }
+          //this.autoCheckIsCharge();
+        }, 3000);
+      }).catch(({err, code, data}) => {
         //toastShort(err);
+        console.log("开始充电错误", err, code);
         Dialog.dismissLoading();
-        this.setState({
-          errorCode: 'A4',
-          showErrorDialog: true,
-          errorMessage: ''+err
-        });
+        if (code == 5200) {
+          this.setState({
+            errorCode: 'none',
+            showErrorDialog: true,
+            errorMessage: "(" + data.transactionPk + ') ' + err
+          });
+        } else {
+          this.setState({
+            errorCode: 'A4',
+            showErrorDialog: true,
+            errorMessage: ''+err
+          });
+        }
       });
     } else {
       this.setState({
         errorCode: 'A1',
         showErrorDialog: true,
-        errorMessage: 'Your vehicle is not connected to the charging station. Please check the connector.'
+        errorMessage: $t('charging.errNotConnected')
       });
     }
   }
+  //开始充电-end
+
+  //停止充电-start
+  onStopCharge() {
+    if (this.state.isCharging) {
+      Dialog.showDialog({
+        title: $t('charging.titleStopCharging'),
+        message: $t('charging.confirmStopCharging'),
+        callback: ok => {
+          if (ok == Dialog.BUTTON_OK) {
+            this.stopCharge();
+          }
+        }
+      });
+    } else {
+      this.stopCharge();
+    }
+  }
 
-  //停止充电api
   stopCharge() {
     this.canAutoRefresh = false;
     Dialog.showProgressDialog();
@@ -703,9 +497,9 @@ export default class TabCharge extends Component {
         }, 3000);
       } else {
         Dialog.dismissLoading();
-        toastShort('An error detected, please retry.')
+        toastShort($t('charging.errDetected'));
       }
-    }).catch(err => {
+    }).catch((err) => {
       Dialog.dismissLoading();
       toastShort(err);
       this.setState({
@@ -722,6 +516,7 @@ export default class TabCharge extends Component {
       });*/
     });
   }
+  //停止充电-end
 
   closeError() {
     this.setState({
@@ -734,10 +529,24 @@ export default class TabCharge extends Component {
     return (
       <View style={styles.container}>
         { this.state.isAuthentic //是否扫码认证
-          ? this.state.isStart   //是否开始充电
-            ? this.StepChargeView()
-            : this.StepStartView()
-          : this.StepRateView()
+        ? <StepChargeView
+            isStart={this.state.isStart} //是否开始充电
+            isPending={this.state.isPending}
+            isCharging={this.state.isCharging}
+            lastUpdated={this.state.lastUpdated}
+            connectorInfo={this.state.connectorInfo}
+            currentPayment={this.state.currentPayment}
+            curerntPerUser={this.state.curerntPerUser}
+            onStartCharge={() => this.startCharge()}
+            onStopCharge={() => this.onStopCharge()}
+            onPaymentMethodChanged={(type) => this.onPaymentMethodChanged(type)}/>
+        : <StepStartView
+            isPrivate={this.state.isPrivate}
+            canIntoCharging={this.state.canIntoCharging}
+            stationInfo={this.state.stationInfo}
+            onEnterStation={() => this.onEnterStation()}
+            onIntoCharging={() => this.toChargingPage()}
+          />
         }
         <ErrorDialog
           visible={this.state.showErrorDialog}
@@ -770,67 +579,5 @@ const styles = StyleSheet.create({
     fontWeight: 'bold',
     paddingTop: 16,
     paddingBottom: 16
-  },
-  gstText: {
-    color: '#EF3340',
-    fontSize: 16,
-    paddingTop: 16,
-    fontWeight: 'bold',
-    textAlign: 'center'
-  },
-  listView: {
-    padding: 8,
-  },
-  batteryBorder: {
-    margin: 30,
-    padding: 32,
-    width: circleSize,
-    height: circleSize,
-    alignItems: 'center',
-    justifyContent: 'center'
-  },
-  buttonView: {
-    marginTop: 16,
-    marginBottom: 32
-  },
-  buttonGroup: {
-    marginTop: 16,
-    marginBottom: 16,
-    alignItems: 'center',
-    flexDirection: 'row'
-  },
-  buttonLeft: {
-    flex: 1,
-    elevation: 1.5,
-  },
-  buttonRight: {
-    flex: 1,
-    marginLeft: 16,
-    elevation: 1.5
-  },
-  inUse: {
-    color: '#fff',
-    fontSize: 12,
-    paddingTop: 4,
-    paddingLeft: 8,
-    paddingRight: 8,
-    paddingBottom: 4,
-    borderRadius: 4,
-    backgroundColor: '#FF7A00'
-  },
-  updateTip: {
-    color: '#aaa',
-    fontSize: 10,
-    textAlign: 'center',
-    paddingTop: 32,
-    paddingBottom: 16
-  },
-  privateView: {
-    height: $vht(25),
-    alignItems: 'center',
-    justifyContent: 'center'
-  },
-  privateText: {
-    color: '#FA5759'
   }
 })

+ 8 - 8
Strides-APP/app/pages/chargeV2/TabInfos.js

@@ -32,40 +32,40 @@ export default class TabInfos extends Component {
     } else if (this.state.stationInfo?.operatingHours) {
       return this.state.stationInfo?.operatingHours;
     } else {
-      return 'To be updated';
+      return $t('charging.toBeUpdated');
     }
   }
 
   getParkingFee() {
     if (this.state.stationInfo?.parkingFeeFree) {
-      return "Free";
+      return $t('charging.free');
     } else if (this.state.stationInfo?.parkingFee) {
       return this.state.stationInfo.parkingFee;
     } else {
-      return 'To be updated';
+      return $t('charging.toBeUpdated');
     }
   }
   
   render() {
     return (
       <View style={$padding(0, 16)}>
-        <Text style={styles.title}>Site Name</Text>
+        <Text style={styles.title}>{$t('charging.siteName')}</Text>
         <View style={styles.infoView}>
           <Text style={styles.infoText}>{this.state.stationInfo?.name}</Text>
         </View>
-        <Text style={styles.title}>Address</Text>
+        <Text style={styles.title}>{$t('charging.siteAddress')}</Text>
         <View style={styles.infoView}>
           <Text style={styles.infoText}>{this.state.stationInfo?.address}</Text>
         </View>
-        <Text style={styles.title}>Parking Fees</Text>
+        <Text style={styles.title}>{$t('charging.parkingFees')}</Text>
         <View style={styles.infoView}>
           <Text style={styles.infoText}>{this.getParkingFee()}</Text>
         </View>
-        <Text style={styles.title}>Operating Hours</Text>
+        <Text style={styles.title}>{$t('charging.operatingHours')}</Text>
         <View style={styles.infoView}>
           <Text style={styles.infoText}>{this.getOperatingHours()}</Text>
         </View>
-        <Text style={styles.title}>Additional Information</Text>
+        <Text style={styles.title}>{$t('charging.additionalInformation')}</Text>
         <View style={styles.infoView}>
           <Text style={styles.infoText}>{this.state.stationInfo?.additionalNotes}</Text>
         </View>

+ 31 - 31
Strides-APP/app/pages/chargeV2/TabReserve.js

@@ -116,7 +116,7 @@ export default class TabReserve extends Component {
       } else {
         this.stopCountdown();
       }
-    }).catch(err => {
+    }).catch((err) => {
       this.stopCountdown();
     });
   }
@@ -129,15 +129,15 @@ export default class TabReserve extends Component {
         chargeTypePk: this.state.checkConnector.chargeTypePk
       }).then(res => {
         Dialog.dismissLoading();
-        toastShort('Reserved successfully!');
+        toastShort($t('charging.reservedSuccess'));
         PagerUtil.setBackRefreshing();
         this.getReserve();
-      }).catch(err => {
+      }).catch((err) => {
         Dialog.dismissLoading();
         toastShort(err)
       });
     } else {
-      toastShort("Please select a connnector")
+      toastShort($t('charging.plsSelectConnnector'))
     }
   }
 
@@ -147,9 +147,9 @@ export default class TabReserve extends Component {
       apiCharge.cancelReserve(this.state.userReserve.reservePk).then(res => {
         Dialog.dismissLoading();
         PagerUtil.setBackRefreshing();
-        toastShort('Cancel successfully!');
+        toastShort($t('common.cancelSuccess'));
         this.getReserve();
-      }).catch(err => {
+      }).catch((err) => {
         Dialog.dismissLoading();
         toastShort(err)
       });
@@ -161,10 +161,10 @@ export default class TabReserve extends Component {
     //   showCancelDialog: true
     // });
     Dialog.showDialog({
-      title: 'Cancle Reservation',
-      message: 'Are you sure you want to cancle reservation?',
-      ok: 'YES',
-      cancel: 'NO',
+      title: $t('charging.cancleReservation'),
+      message: $t('charging.comfirmCancleReservation'),
+      ok: $t('nav.yes'),
+      cancel: $t('nav.no'),
       callback: (btn => {
         if (btn == "ok") {
           this.onCancel();
@@ -175,7 +175,7 @@ export default class TabReserve extends Component {
 
   startCountdown() {
     if (this.state.userReserve.reserveEndTimeTimestamp > 0) {
-      PagerUtil.onReserve();
+      PagerUtil.onReserve(this.props);
       const leftId = this.state.leftId;
       this.countdown(leftId);
     } else {
@@ -242,7 +242,7 @@ export default class TabReserve extends Component {
   }
 
   enterStatioinId() {
-    PagerUtil.onEnterStation();
+    PagerUtil.onEnterStation(this.props);
   }
 
   render() {
@@ -260,7 +260,7 @@ export default class TabReserve extends Component {
               : this.reserveView()
             )
           : <View style={[{height: $vh(50)}, ui.flexvc]}>
-              <Text style={{color: textPrimary, fontSize: 14}}>Reservation is not available for this site.</Text>
+              <Text style={{color: textPrimary, fontSize: 14}}>{$t('charging.unallowReservation')}</Text>
             </View>
         }
         <CancelReserveDialog
@@ -292,7 +292,7 @@ export default class TabReserve extends Component {
     return (
       <>
       <View style={{minHeight: $vht(75)}}>
-        <Text style={styles.title}>Choose Connector</Text>
+        <Text style={styles.title}>{$t('charging.chooseConnector')}</Text>
         { this.state.total > 0
           ? this.state.stationInfo.rateList.map((item, index) => {
             const _type = item.type?.indexOf('AC') >= 0 ? 'AC' : 'DC';
@@ -315,22 +315,22 @@ export default class TabReserve extends Component {
                   source={_type == "AC" ? TypeImage.AC : TypeImage.DC}/>
                 <View style={ChargeStyle.infoGroup}>
                   <Text style={ChargeStyle.infoText}>{item.type}</Text>
-                  <Text style={ChargeStyle.infoTitle}>Type</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
                   <Text style={ChargeStyle.infoText}>{item.power}</Text>
-                  <Text style={ChargeStyle.infoTitle}>Power</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
                   <Text style={ChargeStyle.infoText}>{this.getAvailable(_type)}</Text>
-                  <Text style={ChargeStyle.infoTitle}>Available/Total</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelAvailableTotal')}</Text>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
                   { item?.connectorCount?.available > 0
-                    ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>Available</Text>
-                    : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>Unavailable</Text>
+                    ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</Text>
+                    : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</Text>
                   }
-                  <Text style={ChargeStyle.infoTitle}>Status</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</Text>
                 </View>
                 {/* index == this.state.checkIndex
                   ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusSelected]}>Selected</Text>
@@ -345,14 +345,14 @@ export default class TabReserve extends Component {
         }
         { this.state.checkConnector.available
           ? <>
-              <Text style={styles.title}>Choose Rate</Text>
+              <Text style={styles.title}>{$t('charging.chooseRate')}</Text>
               <BadgeSelectItem
                 style={ChargeStyle.stationInfoView}
                 checked={true}>
                 <Image 
                   style={ChargeStyle.infoIcon}
                   source={require('../../images/charge/ic-type-rate.png')}/>
-                <Text style={ChargeStyle.rateText}>Rate</Text>
+                <Text style={ChargeStyle.rateText}>{$t('charging.labelRate')}</Text>
                 <Text style={[ChargeStyle.ratePrice]}>{this.state.checkConnector.rates}</Text>
                 <Text></Text>
                 {/* <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusSelected]}>Selected</Text> */}
@@ -367,7 +367,7 @@ export default class TabReserve extends Component {
       <Button
         style={styles.buttonView}
         elevation={1.5}
-        text='Reserve'
+        text={$t('charging.btnReserve')}
         disabled={this.state.available}
         onClick={() => this.onReserve()}
       />
@@ -392,15 +392,15 @@ export default class TabReserve extends Component {
                   source={info.type?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
                 <View style={ChargeStyle.infoGroup}>
                   <Text style={ChargeStyle.infoText}>{info.type}</Text>
-                  <Text style={ChargeStyle.infoTitle}>Type</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
                   <Text style={ChargeStyle.infoText}>{info.power}</Text>
-                  <Text style={ChargeStyle.infoTitle}>Power</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
                 </View>
                 <View style={ChargeStyle.infoGroup}>
                   <Text style={ChargeStyle.infoBoldNumber}>{this.getAvailable(info.connectorCount)}</Text>
-                  <Text style={ChargeStyle.infoTitle}>Available/Total</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelAvailableTotal')}</Text>
                 </View>
                 <Text></Text>
                 {/* <SelectableIcon selected={true}/> */}
@@ -411,20 +411,20 @@ export default class TabReserve extends Component {
                 <Image 
                   style={ChargeStyle.infoIcon}
                   source={require('../../images/charge/ic-type-rate.png')}/>
-                <Text style={ChargeStyle.rateText}>Rate</Text>
+                <Text style={ChargeStyle.rateText}>{$t('charging.labelRate')}</Text>
                 <Text style={[ChargeStyle.ratePrice]}>{info.rates}</Text>
                 <Text></Text>
                 {/* <SelectableIcon selected={true}/> */}
               </BadgeSelectItem>
             </View>
           }
-          <Text style={styles.timeleftText}>Reservation time left</Text>
+          <Text style={styles.timeleftText}>{$t('charging.reserveTimeLeft')}</Text>
           <View style={styles.timeleftView}>
             <Text style={styles.timeleft}>{this.state.timeLeft}</Text>
           </View>
           <View style={styles.cancelView}>
             <Button
-              text='Cancel Reservation'
+              text={$t('charging.btnCancelReservation')}
               textColor={textButton}
               style={styles.cancelButton}
               viewStyle={styles.cancelButtonView}
@@ -438,14 +438,14 @@ export default class TabReserve extends Component {
         <View style={styles.buttonGroup}>
           <Button
             style={styles.buttonLeft}
-            text='Scan QR'
+            text={$t('charging.scanQR')}
             disabled={this.state.available}
             onClick={() => {
               startPage(PageList.scanqr, {actionDetail: false, id: this.state.stationInfo.id});
             }}/>
           <Button
             style={styles.buttonRight}
-            text='Enter Station ID'
+            text={$t('charging.enterStationId')}
             disabled={this.state.available}
             onClick={() => {
               this.setState({

+ 96 - 0
Strides-APP/app/pages/charging/StationInfoView.js

@@ -0,0 +1,96 @@
+/**
+ * 充电页:扫码之前和扫码之后的站点信息
+ * @邠心vbe on 2023/03/10
+ */
+import React from 'react';
+import { Image, Text, View } from 'react-native';
+import BadgeSelectItem from '../../components/BadgeSelectItem';
+import { ChargeStyle, TypeImage } from '../chargeV2/Charging';
+
+export default StationInfoView = ({connectorInfo={}, isCharging, isPending}) => (
+  <BadgeSelectItem
+    style={ChargeStyle.stationInfoView}
+    checked={true}>
+    {/* <ImageBackground
+      style={{
+        width: 42,
+        height: 42
+      }}
+      source={require('../../images/charge/icon-station-no.png')}>
+      <Text style={{
+        left: 0,
+        right: 0,
+        bottom: 1,
+        fontSize: 8,
+        color: 'white',
+        textAlign: 'center',
+        position: 'absolute'
+      }}>{connectorInfo.connectorId}</Text>
+    </ImageBackground> */}
+    <Image
+      style={ChargeStyle.infoIcon}
+      source={connectorInfo?.chargeType?.indexOf('AC') >= 0 ? TypeImage.AC : TypeImage.DC}/>
+    <View style={ChargeStyle.infoGroup}>
+      <Text
+        numberOfLines={1}
+        ellipsizeMode="tail"
+        style={ChargeStyle.infoText}>{connectorInfo.chargeType}{connectorInfo.wattage}</Text>
+      <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+    </View>
+    <View style={ChargeStyle.infoGroup}>
+      <Text
+        numberOfLines={1}
+        ellipsizeMode="tail"
+        style={ChargeStyle.infoText}>{connectorInfo.rate}/{connectorInfo.rateType}</Text>
+      <Text style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</Text>
+    </View>
+    <View style={ChargeStyle.infoGroup}>
+      <Text
+        numberOfLines={1}
+        ellipsizeMode="tail"
+        style={ChargeStyle.infoText}>{connectorInfo.wattage}kW{/*connectorInfo.rateType*/}</Text>
+      <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+    </View>
+    <View style={ChargeStyle.infoGroup}>
+      { isCharging
+      ? (isPending
+        ? (
+            <Text
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
+              {$t('charging.statusPreparing')}
+            </Text>
+          )
+        : (
+            <Text
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
+              {$t('charging.statusCharging')}
+            </Text>
+          )
+        )
+      : (connectorInfo.isCheckThrough
+        ? (
+            <Text
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={[ChargeStyle.infoStatus, ChargeStyle.statusAuthenticated]}>
+              {$t('charging.statusAuthenticated')}
+            </Text>
+          )
+        : (
+            <Text
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={[ChargeStyle.infoStatus, ChargeStyle.statusError]}>
+              {$t('charging.statusNotConnected')}
+            </Text>
+          )
+        )
+      }
+      <Text style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</Text>
+    </View>
+  </BadgeSelectItem>
+);

+ 117 - 0
Strides-APP/app/pages/charging/StepChargeView.js

@@ -0,0 +1,117 @@
+/**
+ * 充电页:扫码认证之后-充电开始之前
+ * @邠心vbe on 2023/03/10
+ */
+import React from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+import Button from '../../components/Button';
+import { circleSize, DashboardView } from '../chargeV2/Charging';
+import { PaymentList } from '../chargeV2/Payment';
+import StationInfoView from './StationInfoView';
+ 
+export default StepStartView = ({
+  isStart=false,
+  isPending,
+  isCharging,
+  connectorInfo,
+  currentPayment,
+  curerntPerUser,
+  lastUpdated,
+  onStartCharge,
+  onStopCharge,
+  onPaymentMethodChanged
+}) => (
+  <>
+    <View style={{minHeight: $vht(80)}}>
+      <DashboardView
+        isCharging={isCharging}
+        connectorInfo={connectorInfo}/>
+      <Text style={styles.title}>{$t('charging.selectedCharger')}</Text>
+      <StationInfoView
+        isCharging={isCharging}
+        isPending={isPending}
+        connectorInfo={connectorInfo}/>
+      {/* <BatteryView
+        soc={this.state.connectorInfo.batteryPercent}
+        isCharging={this.state.isCharging}
+        isPending={this.state.isPending}
+      /> */}
+      {/* <View style={ui.center}>
+        <ImageBackground
+          style={styles.batteryBorder}
+          source={require('../../images/charge/ic-charge-circle.png')}>
+          <Text style={{
+            color: textPrimary,
+            fontSize: 16,
+            lineHeight: 22,
+            textAlign: 'center'
+          }}>
+            Press<Text style={{padding: 10, fontWeight: 'bold'}}> Start </Text>to begin Charging
+          </Text>
+        </ImageBackground>
+      </View> */}
+      <Text style={styles.title}>{$t('charging.selectPaymentMethod')}</Text>
+      <PaymentList
+        payType={currentPayment}
+        payPerUse={curerntPerUser}
+        onMethodChange={onPaymentMethodChanged}
+      />
+      {/* <Payment 
+        refreshId={this.state.refreshId}
+        payType={this.state.currentPaytype}
+        balance={this.state.curerntPerUser}
+        isPayPerUse={this.state.currentPayment == PAYTYPE.PAY_PER_USE}
+        onMethodChange={() => this.onMethodChange()}
+      /> */}
+      { lastUpdated
+        ? <Text style={styles.updateTip}>{$t('charging.lastUpdatedAt') + lastUpdated + '\n' + $t('charging.pullDownRefresh')}</Text>
+        : <></>
+      }
+    </View>
+    { isStart
+    ? <Button
+        style={styles.buttonView}
+        disabled={isPending}
+        text={isCharging ? $t('charging.btnStopCharging') : $t('charging.btnComplete')}
+        elevation={1.5}
+        onClick={onStopCharge}/>
+    : <Button
+        style={styles.buttonView}
+        text={$t('charging.btnStartCharging')}
+        elevation={1.5}
+        onClick={onStartCharge}/>
+    }
+  </>
+);
+ 
+const styles = StyleSheet.create({
+  title: {
+    color: '#000',
+    fontSize: 14,
+    fontWeight: 'bold',
+    paddingTop: 16,
+    paddingBottom: 16
+  },
+  buttonView: {
+    marginTop: 16,
+    marginBottom: 32
+  },
+  updateTip: {
+    color: '#aaa',
+    fontSize: 10,
+    textAlign: 'center',
+    paddingTop: 32,
+    paddingBottom: 16
+  },
+  listView: {
+    padding: 8,
+  },
+  batteryBorder: {
+    margin: 30,
+    padding: 32,
+    width: circleSize,
+    height: circleSize,
+    alignItems: 'center',
+    justifyContent: 'center'
+  }
+})

+ 165 - 0
Strides-APP/app/pages/charging/StepStartView.js

@@ -0,0 +1,165 @@
+/**
+ * 充电页:扫码认证之后-充电开始之前
+ * @邠心vbe on 2023/03/10
+ */
+import React from 'react';
+import { Image, StyleSheet, Text, View } from 'react-native';
+import Button from '../../components/Button';
+import { ChargeStyle, TypeImage } from '../chargeV2/Charging';
+import { PageList } from '../Router';
+
+export default StepStartView = ({
+  isPrivate,
+  stationInfo={},
+  onEnterStation,
+  canIntoCharging=false,
+  onIntoCharging
+}) => (
+  <View>
+    <View style={{minHeight: $vht(80)}}>
+      <Text style={styles.gstText}>{$t('charging.tipsRatesTax')}</Text>
+      <Text style={styles.title}>{$t('charging.acChargers')} ({(stationInfo?.acConnector?.available ?? "0") + $t('charging.numberAvailable')})</Text>
+      { stationInfo.acRates?.length > 0
+        ? stationInfo.acRates.map((item, index) => {
+            return (
+              <View key={index} style={ChargeStyle.stationInfoView}>
+                <Image
+                  style={ChargeStyle.infoIcon}
+                  source={TypeImage.AC}/>
+                <View style={ChargeStyle.infoGroup}>
+                  <Text style={ChargeStyle.infoText}>{item.type}</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+                </View>
+                <View style={ChargeStyle.infoGroup}>
+                  <Text style={ChargeStyle.infoText}>{item.rates}</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</Text>
+                </View>
+                <View style={ChargeStyle.infoGroup}>
+                  <Text style={ChargeStyle.infoText}>{item.power}</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+                </View>
+                <View style={ChargeStyle.infoGroup}>
+                  { item?.connectorCount?.available > 0
+                    ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</Text>
+                    : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</Text>
+                  }
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</Text>
+                </View>
+              </View>
+            );
+          })
+        : <Text style={ui.noData}>{$t('charging.noRates')}</Text>
+      }
+      <Text style={styles.title}>{$t('charging.dcChargers')} ({(stationInfo?.dcConnector?.available ?? "0") + $t('charging.numberAvailable')})</Text>
+      { stationInfo.dcRates?.length > 0
+        ? stationInfo.dcRates.map((item, index) => {
+            return (
+              <View key={index} style={ChargeStyle.stationInfoView}>
+                <Image
+                  style={ChargeStyle.infoIcon}
+                  source={TypeImage.DC}/>
+                <View style={ChargeStyle.infoGroup}>
+                  <Text style={ChargeStyle.infoText}>{item.type}</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelType')}</Text>
+                </View>
+                <View style={ChargeStyle.infoGroup}>
+                  <Text style={ChargeStyle.infoText}>{item.rates}</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelRate')}</Text>
+                </View>
+                <View style={ChargeStyle.infoGroup}>
+                  <Text style={ChargeStyle.infoText}>{item.power}</Text>
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelPower')}</Text>
+                </View>
+                <View style={ChargeStyle.infoGroup}>
+                  { item?.connectorCount?.available > 0
+                    ? <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable]}>{$t('charging.statusAvailable')}</Text>
+                    : <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusUnavailable]}>{$t('charging.statusUnavailable')}</Text>
+                  }
+                  <Text style={ChargeStyle.infoTitle}>{$t('charging.labelStatus')}</Text>
+                </View>
+              </View>
+            );
+          })
+        : <Text style={ui.noData}>{$t('charging.noRates')}</Text>
+      }
+      { isPrivate &&
+        <View style={styles.privateView}> 
+          <Text style={styles.privateText}>{$t('charging.ratesPrivateNote')}</Text>
+        </View>
+      }
+    </View>
+    {/* <Payment refreshId={refreshId}/> */}
+    { canIntoCharging
+    ? <Button
+        style={styles.buttonView}
+        text={$t('charging.btnIntoCharging')}
+        //disabled={available}
+        onClick={onIntoCharging}/>
+    : <View style={styles.buttonGroup}>
+        <Button
+          style={styles.buttonLeft}
+          text={$t('charging.scanQR')}
+          //disabled={available}
+          onClick={() => {
+            startPage(PageList.scanqr, {actionDetail: false, id: stationInfo?.id});
+          }}/>
+        <Button
+          style={styles.buttonRight}
+          text={$t('charging.enterStationId')}
+          //disabled={available}
+          onClick={onEnterStation}/>
+      </View>
+    }
+  </View>
+);
+
+const styles = StyleSheet.create({
+  gstText: {
+    color: '#EF3340',
+    fontSize: 16,
+    paddingTop: 16,
+    fontWeight: 'bold',
+    textAlign: 'center'
+  },
+  title: {
+    color: '#000',
+    fontSize: 14,
+    fontWeight: 'bold',
+    paddingTop: 16,
+    paddingBottom: 16
+  },
+  updateTip: {
+    color: '#aaa',
+    fontSize: 10,
+    textAlign: 'center',
+    paddingTop: 32,
+    paddingBottom: 16
+  },
+  privateView: {
+    height: $vht(25),
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  privateText: {
+    color: '#FA5759'
+  },
+  buttonGroup: {
+    marginTop: 16,
+    marginBottom: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  buttonView: {
+    marginTop: 16,
+    marginBottom: 32
+  },
+  buttonLeft: {
+    flex: 1,
+    elevation: 1.5,
+  },
+  buttonRight: {
+    flex: 1,
+    marginLeft: 16,
+    elevation: 1.5
+  },
+})

+ 335 - 0
Strides-APP/app/pages/chargingV2/ChargingPage.js

@@ -0,0 +1,335 @@
+/**
+ * 新充电流程:充电主页
+ * @邠心vbe on 2023/06/20
+ */
+import React, { Component } from 'react';
+import { View } from 'react-native';
+import apiCharge from '../../api/apiCharge';
+import Dialog from '../../components/Dialog';
+import { ErrorDialog } from '../chargeV2/InfoDialog';
+import { PaymentDefault } from '../payment/PaymentConfig';
+import { PageList } from '../Router';
+import StepAuth from './StepAuth';
+import StepCharging from './StepCharging';
+import StepStart from './StepStart';
+import StepStop from './StepStop';
+
+export default class ChargingPage extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      isStoping: false,
+      isCharging: false,
+      isAuthentic: false,
+      stationInfo: {},
+      connectorInfo: {},
+      errorCode: 'A9',
+      errorMessage: '',
+      lastUpdated: '',
+      showErrorDialog: false,
+      showStationDialog: false,
+      curerntPerUse: undefined,
+      currentPayment: PaymentDefault.DEFAULT.payType,
+      currentPaytype: PaymentDefault.DEFAULT.payName
+    };
+    this.waitStartCharging = false;
+  }
+
+  componentDidMount() {
+    this.init();
+    console.log("参数", this.props.route.params);
+    if (this.props.route.params.connectorId && this.props.route.params.chargeBoxId) {
+      this.setState({
+        stationInfo: this.props.route.params
+      }, () => {
+        //测试进入
+        //this.testInit();
+        //正常进入
+        this.getConnectorInfo();
+      })
+    }
+  }
+
+  testInit() {
+    this.setState({
+      isCharging: true,
+      connectorInfo: {
+        status: "Initiating"
+      }
+    }, () => {
+      setTimeout(() => {
+        this.getConnectorInfo();
+      }, 2000);
+    })
+  }
+
+  init() {
+    this.setState({
+      isStoping: false,
+      isCharging: false,
+      isAuthentic: false
+    });
+    this.waitAuthentic = false;
+    this.waitStartCharging = false;
+  }
+
+  getConnectorInfo() {
+    //this.init();
+    apiCharge.getConnectorDetail(this.state.stationInfo).then(res => {
+      if (res.data.status) {
+        const state = {
+          isStoping: false,
+          isCharging: false,
+          isAuthentic: false,
+          connectorInfo: {}
+        }
+        state.connectorInfo = res.data;
+        console.log("状态", res.data.status);
+        switch (res.data.status) {
+          case 'Available': //可用的
+          if (this.waitAuthentic) {
+            state.isAuthentic = true;
+            this.refreshChargeData(3000);
+          } else {
+            state.isAuthentic = false;
+          }
+          break;
+          case 'Preparing': //已插入
+            this.waitAuthentic = false;
+            if (this.waitStartCharging) {
+              state.isCharging = true;
+              this.refreshChargeData(3000);
+            } else {
+              state.isAuthentic = true;
+              //this.checkIsCharge();
+            }
+            break;
+          case 'Charging': //正在充电
+            state.isCharging = true;
+            this.waitStartCharging = false;
+            this.refreshChargeData(10000);
+            break;
+          case 'Initiating': //充电确认中
+            state.isCharging = true;
+            this.refreshChargeData();
+            break;
+          case 'SuspendedEVSE':
+            this.setState({
+              errorCode: 'A5',
+              showErrorDialog: true,
+              errorMessage: $t('charging.errUnable2Charge')
+            });
+            break;
+          case 'SuspendedEV': //已连接上但未充电
+            state.isAuthentic = true;
+            //this.refreshChargeData();
+            break;
+          case 'Reserved': //预定中
+            this.setState({
+              errorCode: 'A5',
+              showErrorDialog: true,
+              errorMessage: $t('charging.errUnable2Reserved')
+            });
+            break;
+          case 'Finishing': //已完成
+            if (res.data.chargingPk) {
+              Dialog.showProgressDialog();
+              setTimeout(() => {
+                Dialog.dismissLoading();
+                this.setState({
+                  isStart: false,
+                  isPending: false,
+                  isCharging: false
+                });
+                startPage(PageList.summary, { 
+                  chargingPk: res.data.chargingPk,
+                  id: this.state.stationInfo.id,
+                  name: this.state.stationInfo.name,
+                  address: this.state.stationInfo.address
+                });
+              }, 2000);
+            } else {
+              goBack();
+            }
+            break;
+          default:
+            this.setState({
+              errorCode: 'A4',
+              showErrorDialog: true,
+              errorMessage: $t('charging.errNotChargeE0')
+            });
+            break;
+        }
+        this.setState(state)
+      }
+    })
+  }
+
+  refreshChargeData(time=2000) {
+    //console.log("[刷新获取充电信息]", time);
+    setTimeout(() => {
+      this.getConnectorInfo();
+    }, time);
+  }
+
+  onPaymentMethodChanged(payment) {
+    this.setState({
+      currentPayment: payment
+    })
+  }
+
+  onAuthenticate() {
+    this.waitAuthentic = true;
+    this.setState({
+      isAuthentic: true
+    }, () => {
+      this.refreshChargeData()
+    })
+  }
+
+  onStartCharge() {
+    this.setState({
+      isCharging: true
+    });
+    this.waitStartCharging = true;
+    apiCharge.startCharge(this.state.stationInfo).then(res => {
+      console.log("[开始充电-onStartCharge]", res);
+      setTimeout(() => {
+        this.canAutoRefresh = true;
+        this.refreshChargeData(500);
+        //Dialog.dismissLoading();
+        if (res.msg) {
+          //Dialog.showResultDialog(res.msg)
+          toastShort(res.msg)
+        }
+        //this.autoCheckIsCharge();
+      }, 3000);
+    }).catch(({err, code, data}) => {
+      //toastShort(err);
+      console.log("[开始充电错误]", err, code);
+      //Dialog.dismissLoading();
+      if (code == 5200) {
+        this.setState({
+          errorCode: 'none',
+          showErrorDialog: true,
+          errorMessage: "(" + data.transactionPk + ') ' + err
+        });
+      } else {
+        this.setState({
+          errorCode: 'A4',
+          showErrorDialog: true,
+          errorMessage: ''+err
+        });
+      }
+    });
+  }
+
+  onStopCharge() {
+    Dialog.showDialog({
+      title: $t('charging.titleStopCharging'),
+      message: $t('charging.confirmStopCharging'),
+      callback: ok => {
+        if (ok == Dialog.BUTTON_OK) {
+          this.stopCharge();
+        }
+      }
+    });
+  }
+
+  stopCharge() {
+    this.setState({
+      isStoping: true
+    })
+    //Dialog.showProgressDialog();
+    apiCharge.stopCharge().then(res => {
+      if (res.data.chargingPk) {
+        setTimeout(() => {
+          //Dialog.dismissLoading();
+          if (res.msg) {
+            toastShort(res.msg)
+          }
+          //this.init();
+          startPage(PageList.summary, { 
+            chargingPk: res.data.chargingPk,
+            id: this.state.stationInfo.id,
+            name: this.state.stationInfo.name,
+            address: this.state.stationInfo.address
+          });
+        }, 3000);
+      } else {
+        if (res.msg) {
+          toastShort(res.msg)
+        } else {
+          toastShort($t('charging.errDetected'));
+        }
+        this.refreshChargeData(500);
+      }
+    }).catch((err) => {
+      //Dialog.dismissLoading();
+      toastShort(err);
+      this.setState({
+        isStart: false,
+        isPending: false,
+        isCharging: false
+      });
+      //模拟进入结算页
+      /*startPage(PageList.summary, {
+        chargingPk: 1,
+        id: this.state.stationInfo.id,
+        name: this.state.stationInfo.name,
+        address: this.state.stationInfo.address
+      });*/
+    });
+  }
+
+  closeError() {
+    this.setState({
+      showErrorDialog: false,
+      showStationDialog: false
+    });
+  }
+
+  render() {
+    return (
+      <View style={ui.flex1}>
+        { this.state.isStoping
+        ? <StepStop
+            currentPayment={this.state.currentPayment}
+            curerntPerUse={this.state.curerntPerUse}
+          />
+        : ( this.state.isCharging 
+          ? <StepCharging
+              connectorInfo={this.state.connectorInfo}
+              currentPayment={this.state.currentPayment}
+              curerntPerUse={this.state.curerntPerUse}
+              onStopCharge={() => this.onStopCharge()}
+            />
+          : ( this.state.isAuthentic
+            ? <StepAuth
+                status={this.state.connectorInfo?.status}
+                currentPayment={this.state.currentPayment}
+                curerntPerUse={this.state.curerntPerUse}
+                onStartCharge={() => this.onStartCharge()}
+              />
+            : <StepStart
+                connectorInfo={this.state.connectorInfo}
+                currentPayment={this.state.currentPayment}
+                curerntPerUse={this.state.curerntPerUse}
+                onAuthenticate={() => this.onAuthenticate()}
+                onPaymentMethodChanged={(type) => this.onPaymentMethodChanged(type)}
+              />
+            )
+          )
+        }
+        <ErrorDialog
+          visible={this.state.showErrorDialog}
+          code={this.state.errorCode}
+          message={this.state.errorMessage}
+          onClose={() => {
+            this.closeError();
+          }}
+        />
+      </View>
+    );
+  }
+}

+ 125 - 0
Strides-APP/app/pages/chargingV2/StepAuth.js

@@ -0,0 +1,125 @@
+/**
+ * 新充电流程:验证插头模块
+ * @邠心vbe on 2023/06/20
+ */
+import React, { useEffect, useState } from 'react';
+import { View, Text, Image, StyleSheet } from 'react-native';
+import Button from '../../components/Button';
+import { PaymentList } from '../chargeV2/Payment';
+
+export default StepAuth = ({
+  status="",
+  currentPayment,
+  curerntPerUse,
+  onStartCharge
+}) => {
+  const [loadingEmps, setEmps] = useState("");
+  const [isAuthentic, setAuthentic] = useState(false)
+  useEffect(() => {
+    if (status == "Preparing") {
+      setAuthentic(true);
+    } else {
+      changeEmps();
+    }
+  }, []);
+
+  useEffect(() => {
+    if (status == "Preparing") {
+      setAuthentic(true);
+    } else {
+      setTimeout(() => {
+        changeEmps();
+      }, 500);
+    }
+  }, [loadingEmps, status]);
+
+  const changeEmps = () => {
+    let emp = loadingEmps;
+    if (loadingEmps.length == 3) {
+      emp = "";
+    } else {
+      emp += ".";
+    }
+    setEmps(emp);
+  }
+
+  return (
+    <View style={styles.container}>
+      <View style={styles.content}>
+        <Image
+          style={styles.stepImage}
+          resizeMode="contain"
+          source={
+            isAuthentic
+            ? require('../../images/site/charging-status-auth.png')
+            : require('../../images/site/charging-status-ready.png')
+          }/>
+        { isAuthentic
+        ? <Text style={styles.stepTitle}>{$t('charging.stepAuthenticated')}</Text>
+        : <View style={ui.flexcc}>
+            <Text style={styles.stepTitle}>{$t('charging.stepAuthenticating')}</Text>
+            <Text style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</Text>
+          </View>
+        }
+        <Text style={styles.stepDesc}>{$t(isAuthentic ? 'charging.stepAuthenticatedDesc' : 'charging.stepAuthenticatingDesc')}</Text>
+      </View>
+      <View style={styles.bottomView}>
+        <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+        <PaymentList
+          payType={currentPayment}
+          payPerUse={curerntPerUse}
+        />
+        { isAuthentic
+        ? <Button
+            style={styles.buttonView}
+            text={$t('charging.btnStartCharging')}
+            elevation={1.5}
+            borderRadius={6}
+            onClick={onStartCharge}/>
+        : <View style={{height: 56}}/>
+        }
+      </View>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    padding: 16
+  },
+  content: {
+    flex: 1,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  stepImage: {
+    width: $vw(70),
+    height: $vw(16),
+    margin: 16
+  },
+  stepTitle: {
+    fontSize: 24,
+    fontWeight: 'bold',
+    color: colorAccent
+  },
+  stepDesc: {
+    color: textPrimary,
+    fontSize: 16,
+    textAlign: 'center',
+    ...$padding(0, 32, 48)
+  },
+  label: {
+    color: '#000',
+    fontSize: 14,
+    fontWeight: 'bold',
+    paddingTop: 16,
+    paddingBottom: 8
+  },
+  bottomView: {
+    paddingBottom: 16
+  },
+  buttonView: {
+    marginTop: 8
+  }
+})

+ 285 - 0
Strides-APP/app/pages/chargingV2/StepCharging.js

@@ -0,0 +1,285 @@
+/**
+ * 新充电流程:正在充电模块
+ * @邠心vbe on 2023/06/20
+ */
+import React, { useEffect, useRef, useState } from 'react';
+import { Animated, Easing, Image, ScrollView, StyleSheet, Text, View } from 'react-native';
+import Button, { ElevationObject } from '../../components/Button';
+import utils from '../../utils/utils';
+import { PaymentList } from '../chargeV2/Payment';
+
+const StepCharging = ({
+  connectorInfo={},
+  currentPayment,
+  curerntPerUse,
+  onStopCharge
+}) => {
+  const [isCharging, setCharging] = useState(false);
+  const [loadingEmps, setEmps] = useState("");
+  const [skipAnim, setAnimSkip] = useState(false);
+  const topAnim = useRef(new Animated.Value($vw(16)*-2)).current;
+  const fadeAnim = useRef(new Animated.Value(0)).current;
+
+  const moveTop = () => {
+    fadeAnim.setValue(0);
+    Animated.timing(topAnim, {
+      toValue: ($vh(50) * -1 - $vw(16) - 32),
+      duration: 1000,
+      easing: Easing.linear,
+      useNativeDriver: false
+    }).start();
+    setTimeout(() => {
+      setCharging(true);
+      showFade();
+    }, 1100);
+  }
+
+  const showFade = () => {
+    Animated.timing(fadeAnim, {
+      toValue: 1,
+      duration: 1000,
+      useNativeDriver: false
+    }).start();
+    setTimeout(() => {
+      setAnimSkip(true);
+    }, 1100);
+  }
+
+  useEffect(() => {
+    const isCharge = (connectorInfo.status == "Charging");
+    console.log("[useEffect]:start", isCharge, fadeAnim);
+    setCharging(isCharge);
+    setAnimSkip(isCharge);
+    if (!isCharge) {
+      changeEmps();
+    }
+  }, [])
+
+  useEffect(() => {
+    //console.log("[useEffect]:connectorInfo", connectorInfo.status);
+    if (connectorInfo.status == "Charging") {
+      if (!isCharging) {
+        moveTop();
+      }
+    } else {
+      setCharging(false)
+    }
+  }, [connectorInfo])
+
+  useEffect(() => {
+    //console.log("[useEffect]:loadingEmps", loadingEmps);
+    if (connectorInfo.status == "Charging") {
+      moveTop();
+    } else {
+      setTimeout(() => {
+        changeEmps();
+      }, 500);
+    }
+  }, [loadingEmps])
+
+  const changeEmps = () => {
+    let emp = loadingEmps;
+    if (loadingEmps.length == 3) {
+      emp = "";
+    } else {
+      emp += ".";
+    }
+    setEmps(emp);
+  }
+
+  return (
+  isCharging
+  ? <ScrollView
+      style={ui.flex1}
+      contentContainerStyle={$padding(16)}>
+      <Animated.View style={{opacity: skipAnim ? 1 : fadeAnim}}>
+        <View style={ui.center}>
+          <Image
+            style={styles.stepImage}
+            resizeMode="contain"
+            source={require('../../images/site/charging-status-charge.png')}/>
+          <Text style={styles.stepTitle}>{$t('charging.statusCharging')}</Text>
+          <Text style={styles.stepDesc}>{$t('charging.stepChargingDesc')}</Text>
+          { connectorInfo.batteryPercent >= 0 &&
+            <Text
+              style={styles.socText}
+              numberOfLines={1}>{connectorInfo.batteryPercent || "0"}%</Text>
+          }
+        </View>
+        <View style={styles.infoRow}>
+          <View style={skipAnim ? styles.infoCarded : styles.infoCard}>
+            <Text style={styles.infoTitle}>{$t('charging.labelTimeElapsed')}</Text>
+            <Text
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.infoText}>{utils.minutes2HHMM(connectorInfo?.timeElapsed ?? 0)}</Text>
+            <Text style={styles.infoDesc}></Text>
+          </View>
+          <View style={skipAnim ? styles.infoCarded : styles.infoCard}>
+            <Text style={styles.infoTitle}>{$t('charging.labelTotalkWh')}</Text>
+            <Text
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.infoText}>{connectorInfo.totalKWhDelivered || "0"} kWh</Text>
+            <Text style={styles.infoDesc}></Text>
+          </View>
+        </View>
+        <View style={styles.infoRow}>
+          <View style={skipAnim ? styles.infoCarded : styles.infoCard}>
+            <Text style={styles.infoTitle}>{$t('charging.labelRate')}</Text>
+            <Text
+              style={styles.infoText}>{connectorInfo.rates || "S$0.00/kWh"}</Text>
+            <Text
+              style={styles.infoDesc}>({connectorInfo.userRates || "S$0.00/kWh"})</Text>
+          </View>
+          <View style={skipAnim ? styles.infoCarded : styles.infoCard}>
+            <Text style={styles.infoTitle}>{$t('charging.labelTotalCharges')}</Text>
+            <Text
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.infoText}>{connectorInfo.totalCharges || "S$ 0.0"}</Text>
+            <Text
+              numberOfLines={1}
+              ellipsizeMode="tail"
+              style={styles.infoDesc}>({connectorInfo.userTotalCharges || "S$ 0.0"})</Text>
+          </View>
+        </View>
+      </Animated.View>
+      <EndView/>
+      <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+      <PaymentList
+        payType={currentPayment}
+        payPerUse={curerntPerUse}
+      />
+      <Button
+        style={styles.buttonView}
+        text={$t('charging.btnStopCharging')}
+        elevation={1.5}
+        borderRadius={6}
+        onClick={onStopCharge}/>
+    </ScrollView>
+  : <View style={ui.flex1}>
+      <Animated.View style={[
+        styles.content, {
+          marginTop: topAnim
+        }
+      ]}>
+        <Image
+          style={styles.stepImage}
+          resizeMode="contain"
+          source={require('../../images/site/charging-status-ready.png')}/>
+        <View style={ui.flexcc}>
+          <Text style={styles.stepTitle}>{$t('charging.stepInitializing')}</Text>
+          <Text style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</Text>
+        </View>
+        <Text style={styles.stepDesc}>{$t('charging.stepInitializingDesc')}</Text>
+      </Animated.View>
+      <View style={styles.bottomView}>
+        <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+        <PaymentList
+          payType={currentPayment}
+          payPerUse={curerntPerUse}
+        />
+        <View style={styles.buttonView}></View>
+      </View>
+    </View>
+  );
+}
+
+export default StepCharging;
+
+const styles = StyleSheet.create({
+  content: {
+    flex: 1,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  stepImage: {
+    width: $vw(70),
+    height: $vw(16),
+    margin: 16
+  },
+  stepTitle: {
+    fontSize: 24,
+    fontWeight: 'bold',
+    color: colorAccent
+  },
+  stepDesc: {
+    color: textPrimary,
+    fontSize: 16,
+    textAlign: 'center',
+    ...$padding(0, 32, 48)
+  },
+  socText: {
+    right: $vw(15),
+    color: colorAccent,
+    width: 60,
+    marginRight: -8,
+    fontSize: 24,
+    fontWeight: 'bold',
+    position: 'absolute',
+    textAlign: 'center',
+  },
+  infoRow: {
+    marginLeft: 16,
+    marginBottom: 16,
+    flexDirection: 'row'
+  },
+  infoCard: {
+    flex: 1,
+    padding: 12,
+    borderRadius: 10,
+    marginRight: 16,
+    alignItems: 'center',
+    //...ElevationObject(5),
+    backgroundColor: colorLight
+  },
+  infoCarded: {
+    flex: 1,
+    padding: 12,
+    borderRadius: 10,
+    marginRight: 16,
+    alignItems: 'center',
+    ...ElevationObject(5),
+    backgroundColor: colorLight
+  },
+  infoTitle: {
+    color: textPrimary,
+    fontSize: 12,
+    paddingTop: 1
+  },
+  infoText: {
+    color: textPrimary,
+    fontSize: 16,
+    fontWeight: 'bold',
+    ...$padding(16)
+  },
+  infoDesc: {
+    color: textPrimary,
+    fontSize: 12,
+    marginTop: -16,
+    paddingBottom: 8
+  },
+  infoStatus: {
+    fontSize: 16,
+    fontWeight: 'bold',
+    ...$padding(16, 8)
+  },label: {
+    color: '#000',
+    fontSize: 14,
+    fontWeight: 'bold',
+    paddingTop: 16,
+    paddingBottom: 8
+  },
+  bottomView: {
+    left: 0,
+    right: 0,
+    bottom: 32,
+    padding: 16,
+    position: 'absolute'
+  },
+  buttonView: {
+    marginTop: 16,
+    marginBottom: 16
+  }
+})

+ 151 - 0
Strides-APP/app/pages/chargingV2/StepStart.js

@@ -0,0 +1,151 @@
+/**
+ * 新充电流程:充电前的模块
+ * @邠心vbe on 2023/06/20
+ */
+import React from 'react';
+import { View, Text, Image, StyleSheet, ScrollView } from 'react-native';
+import Button, { ElevationObject } from '../../components/Button';
+import { ChargeStyle } from '../chargeV2/Charging';
+import { PaymentList } from '../chargeV2/Payment';
+
+export default StepStart = ({
+  connectorInfo={},
+  currentPayment,
+  curerntPerUse,
+  onPaymentMethodChanged,
+  onAuthenticate
+}) => {
+  return (
+    <ScrollView
+      style={ui.flex1}
+      contentContainerStyle={$padding(16)}>
+      <View style={ui.center}>
+        <Image
+          style={styles.stepImage}
+          resizeMode="contain"
+          source={require('../../images/site/charging-status-unknow.png')}/>
+        <Text style={styles.stepTitle}>{$t('charging.stepInsertConnector')}</Text>
+        <Text style={styles.stepDesc}>{$t('charging.stepInsertConnectorDesc')}</Text>
+      </View>
+      <View style={styles.infoRow}>
+        <View style={styles.infoCard}>
+          <Text style={styles.infoTitle}>{$t('charging.labelType')}</Text>
+          <Text
+            numberOfLines={1}
+            ellipsizeMode="tail"
+            style={styles.infoText}>{connectorInfo.chargeType}{connectorInfo.wattage || "AC22"}</Text>
+          <Text style={styles.infoDesc}></Text>
+        </View>
+        <View style={styles.infoCard}>
+          <Text style={styles.infoTitle}>{$t('charging.labelPower')}</Text>
+          <Text
+            numberOfLines={1}
+            ellipsizeMode="tail"
+            style={styles.infoText}>{connectorInfo.wattage || "0"} kW{/*connectorInfo.rateType*/}</Text>
+          <Text style={styles.infoDesc}></Text>
+        </View>
+      </View>
+      <View style={styles.infoRow}>
+        <View style={styles.infoCard}>
+          <Text style={styles.infoTitle}>{$t('charging.labelRate')}</Text>
+          {/* <Text
+            numberOfLines={1}
+            ellipsizeMode="tail"
+            style={styles.infoText}>{connectorInfo.rate || "0.00"}/{connectorInfo.rateType || "kWh"}</Text> */}
+          <Text
+            style={styles.infoText}>{connectorInfo.rates || "S$0.00/kWh"}</Text>
+          <Text
+              style={styles.infoDesc}>({connectorInfo.userRates || "S$0.00/kWh"})</Text>
+        </View>
+        <View style={styles.infoCard}>
+          <Text style={styles.infoTitle}>{$t('charging.labelStatus')}</Text>
+          <Text
+            style={[ChargeStyle.statusAuthenticated, styles.infoStatus]}>
+            {$t('charging.statusAvailable')}
+          </Text>
+        </View>
+      </View>
+      <EndView/>
+      <EndView half/>
+      <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+      <PaymentList
+        payType={currentPayment}
+        payPerUse={curerntPerUse}
+        onMethodChange={onPaymentMethodChanged}
+      />
+      <Button
+        style={styles.buttonView}
+        text={$t('charging.btnAuthenticate')}
+        elevation={1.5}
+        borderRadius={6}
+        onClick={onAuthenticate}/>
+    </ScrollView>
+  );
+}
+
+const styles = StyleSheet.create({
+  stepImage: {
+    width: $vw(70),
+    height: $vw(16),
+    margin: 16
+  },
+  stepTitle: {
+    fontSize: 24,
+    fontWeight: 'bold',
+    color: colorAccent
+  },
+  stepDesc: {
+    color: textPrimary,
+    fontSize: 16,
+    textAlign: 'center',
+    ...$padding(0, 32, 48)
+  },
+  infoRow: {
+    marginLeft: 16,
+    marginBottom: 16,
+    flexDirection: 'row'
+  },
+  infoCard: {
+    flex: 1,
+    padding: 12,
+    borderRadius: 10,
+    marginRight: 16,
+    alignItems: 'center',
+    ...ElevationObject(5),
+    backgroundColor: colorLight,
+  },
+  infoTitle: {
+    color: textPrimary,
+    fontSize: 12,
+    paddingTop: 1
+  },
+  infoText: {
+    color: textPrimary,
+    fontSize: 16,
+    textAlign: 'center',
+    fontWeight: 'bold',
+    ...$padding(16, 0)
+  },
+  infoDesc: {
+    color: textPrimary,
+    fontSize: 12,
+    marginTop: -16,
+    paddingBottom: 8
+  },
+  infoStatus: {
+    fontSize: 16,
+    fontWeight: 'bold',
+    ...$padding(16, 8)
+  },
+  label: {
+    color: '#000',
+    fontSize: 14,
+    fontWeight: 'bold',
+    paddingTop: 16,
+    paddingBottom: 8
+  },
+  buttonView: {
+    marginTop: 8,
+    marginBottom: 16
+  }
+})

+ 101 - 0
Strides-APP/app/pages/chargingV2/StepStop.js

@@ -0,0 +1,101 @@
+/**
+ * 新充电流程:停止充电模块
+ * @邠心vbe on 2023/06/20
+ */
+import React, { useEffect, useState } from 'react';
+import { View, Text, Image, StyleSheet } from 'react-native';
+import { PaymentList } from '../chargeV2/Payment';
+
+export default StepStop = ({
+  currentPayment,
+  curerntPerUse
+}) => {
+  const [loadingEmps, setEmps] = useState("");
+
+  useEffect(() => {
+    changeEmps();
+  }, []);
+
+  useEffect(() => {
+    setTimeout(() => {
+      changeEmps();
+    }, 500);
+  }, [loadingEmps]);
+
+  const changeEmps = () => {
+    let emp = loadingEmps;
+    if (loadingEmps.length == 3) {
+      emp = "";
+    } else {
+      emp += ".";
+    }
+    setEmps(emp);
+  }
+
+  return (
+    <View style={styles.container}>
+      <View style={styles.content}>
+        <Image
+          style={styles.stepImage}
+          resizeMode="contain"
+          source={require('../../images/site/charging-status-ready.png')}
+        />
+        <View style={ui.flexcc}>
+          <Text style={styles.stepTitle}>{$t('charging.stepStoppingCharge')}</Text>
+          <Text style={[styles.stepTitle, {width: 30, marginRight: -10}]}>{loadingEmps}</Text>
+        </View>
+        <Text style={styles.stepDesc}>{$t('charging.stepStoppingChargeDesc')}</Text>
+      </View>
+
+      <View style={styles.bottomView}>
+      <Text style={styles.label}>{$t('charging.paymentMethod')}</Text>
+        <PaymentList
+          payType={currentPayment}
+          payPerUse={curerntPerUse}
+        />
+        <View style={{height: 56}}/>
+      </View>
+    </View>
+  )
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    padding: 16
+  },
+  content: {
+    flex: 1,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  stepImage: {
+    width: $vw(70),
+    height: $vw(16),
+    margin: 16
+  },
+  stepTitle: {
+    fontSize: 24,
+    fontWeight: 'bold',
+    color: colorAccent
+  },
+  stepDesc: {
+    color: textPrimary,
+    fontSize: 16,
+    textAlign: 'center',
+    ...$padding(0, 32, 48)
+  },
+  label: {
+    color: '#000',
+    fontSize: 14,
+    fontWeight: 'bold',
+    paddingTop: 16,
+    paddingBottom: 8
+  },
+  bottomView: {
+    paddingBottom: 16
+  },
+  buttonView: {
+    marginTop: 8
+  }
+})

+ 54 - 19
Strides-APP/app/pages/home/Drawer.js

@@ -138,11 +138,13 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
       } else if (res.msg) {
         toastShort(res.msg);
       } else {
-        toastShort('No charging session found');
+        toastShort($t("drawer.noChargingSession"));
       }
-    }).catch(err => {
+    }).catch((err) => {
+      if (app.debug)
+        console.log(err);
       Dialog.dismissLoading();
-      toastShort('No charging session found');
+      toastShort($t("drawer.noChargingSession"));
     })
   }
 
@@ -187,8 +189,8 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             { isLogin
               ? userInfo.nickName 
                 ? userInfo.nickName
-                : 'Logging...'
-              : "Sign In"
+                : $t('drawer.logging')
+              : $t('drawer.sign')
             }
           </Text>
           <FontAwesome
@@ -249,7 +251,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             {/* <Image
               style={styles.icon}
               source={require('../../images/icon/draw-charge.png')}/> */}
-            <Text style={styles.label}>Charging</Text>
+            <Text style={styles.label}>{$t('drawer.charging')}</Text>
           </Button>
         : <View
             style={styles.disableItem}>
@@ -262,7 +264,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             {/* <Image
               style={styles.icon}
               source={require('../../images/icon/draw-charge-no.png')}/> */}
-            <Text style={styles.disable}>Charging</Text>
+            <Text style={styles.disable}>{$t('drawer.charging')}</Text>
           </View>
       }
       {/* isLogin
@@ -275,14 +277,14 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             <Image
               style={styles.icon}
               source={require('../../images/icon/draw-transaction.png')}/>
-            <Text style={styles.label}>Transactions</Text>
+            <Text style={styles.label}>{$t('drawer.wallet')}</Text>
           </Button>
         : <View
             style={styles.disableItem}>
             <Image
               style={styles.icon}
               source={require('../../images/icon/draw-transaction-no.png')}/>
-            <Text style={styles.disable}>Transactions</Text>
+            <Text style={styles.disable}>{$t('drawer.wallet')}</Text>
           </View>
       */}
       {
@@ -296,7 +298,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
           <Image
             style={styles.icon}
             source={require('../../images/icon/draw-transaction.png')}/>
-          <Text style={styles.label}>Transactions</Text>
+          <Text style={styles.label}>{$t('drawer.wallet')}</Text>
         </Button>
         <Button
           style={styles.itemButton}
@@ -307,11 +309,44 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             size={26}
             name={"wallet-outline"}
             color={colorDark}/>
-          <Text style={styles.label}>Credits</Text>
+          <Text style={styles.label}>{$t('drawer.topup')}</Text>
           <Text style={styles.balanceText2}>{currency}{userInfo.credit}</Text>
         </Button></>
       }
 
+      {/*附加功能-开始*/}
+      { app.modules.notifications &&
+        <Button
+          style={styles.itemButton}
+          viewStyle={styles.itemView}
+          onClick={() => {
+            startPage(PageList.bookmarks);
+          }}>
+          <MaterialIcons
+            style={styles.icon}
+            name="stars"
+            color="#222"
+            size={26}/>
+          <Text style={styles.label}>{$t('route.bookmarks')}</Text>
+        </Button>
+      }
+      { app.modules.bookmarks &&
+        <Button
+          style={styles.itemButton}
+          viewStyle={styles.itemView}
+          onClick={() => {
+            startPage(PageList.bookmarks);
+          }}>
+          <MaterialIcons
+            style={styles.icon}
+            name="stars"
+            color="#222"
+            size={26}/>
+          <Text style={styles.label}>{$t('route.bookmarks')}</Text>
+        </Button>
+      }
+      {/*附加功能-结束*/}
+
       <Button
         style={styles.itemButton}
         viewStyle={styles.itemView}
@@ -323,7 +358,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
           name="message-alert-outline"
           color="#222"
           size={24}/>
-        <Text style={styles.label}>Feedback</Text>
+        <Text style={styles.label}>{$t('drawer.feedback')}</Text>
       </Button>
 
       {/* <Button
@@ -352,7 +387,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
           color="#222"
           size={25}
         />
-        <Text style={styles.label}>Settings</Text>
+        <Text style={styles.label}>{$t('drawer.settings')}</Text>
       </Button>
       <Button
         style={styles.itemButton}
@@ -366,14 +401,14 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
           color="#333"
           size={26}
         />
-        <Text style={styles.label}>About Us</Text>
+        <Text style={styles.label}>{$t('drawer.about')}</Text>
       </Button>
 
       {/* <View style={styles.divided}></View> */}
       { DEBUG &&
       <>
         <View style={styles.divideLogin}></View>
-        <Text style={{color: "#ccc", paddingLeft: 16, paddingBottom: 8, fontSize: 12}}>Debug Mode Options Only</Text>
+        <Text style={{color: "#ccc", paddingLeft: 16, paddingBottom: 8, fontSize: 12}}>{$t('drawer.debugOnly')}</Text>
         <Button
           style={styles.itemButton}
           viewStyle={styles.itemView}
@@ -385,7 +420,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             name="file-eye-outline"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>Privacy Policy</Text>
+          <Text style={styles.label}>{$t('drawer.privacyPolicy')}</Text>
         </Button>
         <Button
           style={styles.itemButton}
@@ -398,7 +433,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             name="file-eye-outline"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>Terms of Use</Text>
+          <Text style={styles.label}>{$t('drawer.termsOfUse')}</Text>
         </Button>
         <Button
           style={styles.itemButton}
@@ -414,7 +449,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             name="notifications-none"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>Notification Test</Text>
+          <Text style={styles.label}>{$t('route.notificationTest')}</Text>
         </Button>
         <Button
           style={styles.itemButton}
@@ -427,7 +462,7 @@ const DrawerContent = ({isLogin, userInfo, onLogout, navigation}) => {
             name="map-legend"
             color="#222"
             size={26}/>
-          <Text style={styles.label}>Maps Test</Text>
+          <Text style={styles.label}>{$t('drawer.mapsTest')}</Text>
         </Button>
       </>}
 

+ 193 - 136
Strides-APP/app/pages/home/Home.js

@@ -4,18 +4,20 @@
  */
 import React, { Component } from 'react';
 import { View, Text, Pressable, Image, StyleSheet, BackHandler, Linking } from 'react-native';
-import {check, request, openSettings, PERMISSIONS, RESULTS} from 'react-native-permissions';
 import apiBase from '../../api/apiBase';
 import apiStation from '../../api/apiStation';
 import Dialog from '../../components/Dialog';
 import { Styles } from '../../components/Toolbar';
+import MyStatusBar from '../../components/MyStatusBar';
 import utils from '../../utils/utils';
 import { PageList } from '../Router';
 import { SettingUtil } from '../Settings';
 import BottomSiteInfo from './maps/BottomSiteInfo';
+import LocationPermission from './maps/LocationPermission';
 import Maps from './maps/Maps';
 import SearchTool from './maps/SearchTool';
 import app from '../../../app.json'
+import { i18nUtil } from '../../i18n';
 
 export default class HomePage extends Component {
   constructor(props) {
@@ -30,14 +32,15 @@ export default class HomePage extends Component {
       mapReady: false,
       stationInfo: {},
       stopList: [],
-      hasPermission: isIOS
+      hasPermission: isIOS,
+      permissionDenied: false
     };
     this.isHide = true;
     this.denied = true;
     this.backSeconds = 0;
     this.refreshTime = 0;
     this.settingInfo = {}
-    //this.unsubscribe = null;
+    this.viewIndex = -1;
     this.filter = {
       parkingFee: 'ALL',
       connectorType: ''
@@ -45,47 +48,51 @@ export default class HomePage extends Component {
   }
 
   componentDidMount() {
-    console.log("componentDidMount")
     const navigation = this.props.navigation;
     navigation.addListener('focus', () => {
       //toastShort('onResume')
       this.setState({
         stationInfo: {}
       });
+      this.viewIndex = -1;
       SettingUtil.getSettings(set => {
-        console.log("获取设置信息", set);
+        //console.log("获取设置信息", set);
         this.settingInfo = set;
         this.checkPermission2Geo();
       })
-      console.log("focus");
       this.isHide = false;
+      MyStatusBar.setStatusBarThemes(MyStatusBar.DARK_STYLE, colorLight);
     });
 
     navigation.addListener('blur', () => {
       //toastShort('onStop')
       this.isHide = true;
+      MyStatusBar.setStatusBarThemes(MyStatusBar.LIGHT_STYLE, colorPrimaryDark);
       setTimeout(() => {
         navigation.closeDrawer();
       }, 200);
     });
-
-    /*this.unsubscribe = navigation.addListener('beforeRemove', (e) => {
-      this.toExit(e);
+    
+    /*navigation.addListener('beforeRemove', (e) => {
+      if (isIOS) {
+        e.preventDefault();
+      } else {
+        let time = new Date().getTime();
+        if (time - this.backSeconds < 2000) {
+          BackHandler.exitApp();
+        } else {
+          toastShort("Press the back button again to exit the program");
+          this.backSeconds = time;
+          e.preventDefault();
+        }
+      }
     });*/
     BackHandler.addEventListener('hardwareBackPress', this.toExit)
     this.isHide = false;
+    MyStatusBar.setStatusBarThemes(MyStatusBar.DARK_STYLE, colorLight);
     this.checkUpdateVersion();
   }
 
-  /*componentDidUpdate() {
-    console.log("componentDidUpdate", this.unsubscribe);
-    if (!this.unsubscribe) {
-      this.unsubscribe = this.props.navigation.addListener('beforeRemove', (e) => {
-        this.toExit(e);
-      });
-    }
-  }*/
-
   componentWillUnmount() {
     /*console.log("componentWillUnmount")
     if (this.unsubscribe) {
@@ -119,23 +126,26 @@ export default class HomePage extends Component {
     apiBase.checkUpdate().then(res => {
       if (res.data.versionCode > app.versionCode) {
         var desc =  JSON.parse(res.data?.versionDesc ?? "{}");
-        if (desc && typeof desc.en == "string") {
-          Dialog.showDialog({
-            title: "Version Update",
-            message: "New Version: " + res.data.versionName + "\n\n" + desc.en,
-            showCancel: !(res.data.force),
-            ok: "UPDATE NOW",
-            cancel: "LATER",
-            callback: btn => {
-              if (btn == Dialog.BUTTON_OK) {
-                //TODO 跳转到应用商店
-                const uri = isIOS
-                ? 'itms-appss://apps.apple.com/app/id1664718768'
-                : 'market://details?id=com.strides.chargeco'
-                Linking.openURL(uri);
+        if (desc) {
+          const upLog = i18nUtil.analyzeLocaleData(desc);
+          if (typeof upLog == "string") {
+            this.showUpdateDialog({
+              title: $t("home.versionUpdate"),
+              message: $t("home.newVersionName") + res.data.versionName + "\n\n" + upLog,
+              showCancel: !(res.data.force),
+              ok: $t("home.updateNow"),
+              cancel: $t("home.later"),
+              callback: btn => {
+                if (btn == Dialog.BUTTON_OK) {
+                  //TODO 跳转到应用商店
+                  const uri = isIOS
+                  ? app.storeUrl.ios
+                  : app.storeUrl.android
+                  Linking.openURL(uri);
+                }
               }
-            }
-          })
+            })
+          }
         }
       }
     }).catch(err => {
@@ -143,20 +153,27 @@ export default class HomePage extends Component {
     })
   }
 
+  showUpdateDialog(options) {
+    if (Dialog.isShowing()) {
+      setTimeout(() => {
+        this.showUpdateDialog(options)
+      }, 500);
+      return;
+    }
+    Dialog.showDialog(options)
+  }
+
   getPermission() {
-    request(
-      isIOS 
-    ? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
-    : PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION)
-    .then(res => {
-      console.log('getPermission', res)
-      this.setState({
-        hasPermission: true
-      });
-      this.checkPermission2Geo();
-    }).catch(err => {
-      console.info('getPermission', err)
-    });
+    LocationPermission.requestPermission(hasPermission => {
+      if (hasPermission) {
+        this.setState({
+          hasPermission: true
+        });
+        this.checkPermission2Geo();
+      } else {
+        this.noPermissionSite();
+      }
+    })
   }
 
   checkPermission2Geo(refresh) {
@@ -166,71 +183,44 @@ export default class HomePage extends Component {
         this.state.stopList = []
       }
       this.infoGeoLocation();
-      return;
-    }
-    check(
-      isIOS 
-    ? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
-    : PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION)
-    .then(res => {
-      //console.log('checkPermission2Geo', res);
-      switch (res) {
-        case RESULTS.UNAVAILABLE:
-          console.log('此功能不可用(在此设备上/在此上下文中)');
-          if (isIOS) {
-            this.setState({
-              hasPermission: true
-            });
-            this.infoGeoLocation();
-          }
-          break;
-        case RESULTS.DENIED:
-          console.log('权限未被请求/被拒绝,但可以请求');
-          if (this.denied) {
-            this.denied = false;
-            this.getPermission();
-          } else {
-            this.denied = true;
-          }
-          break;
-        case RESULTS.LIMITED:
-          console.log('权限是有限的:有些操作是可能的');
+    } else {
+      LocationPermission.checkPermission((hasPermission, canRequestPermission) => {
+        if (hasPermission) {
           this.setState({
             hasPermission: true
           });
           this.infoGeoLocation();
-          break;
-        case RESULTS.GRANTED:
-          console.log('许可被授予');
-          this.setState({
-            hasPermission: true
-          });
-          this.infoGeoLocation();
-          break;
-        case RESULTS.BLOCKED:
-          console.log('权限被拒绝,不再可请求');
-          Dialog.showDialog({
-            title: 'Error',
-            message: 'Can not get charge station, Please grant location permissions.',
-            ok: 'SETTING',
-            callback: btn => {
-              if (btn == Dialog.BUTTON_OK) {
-                console.log('ok');
-                openSettings().catch(() => console.warn('cannot open settings'));
-              }
+        } else {
+          if (canRequestPermission) {
+            if (this.denied) {
+              this.denied = false;
+              this.getPermission();
+            } else {
+              //避免多次请求
+              this.denied = true;
+              this.noPermissionSite();
             }
-          });
-          //toastShort('Save to gallery failed');
-          break;
-      }
-    }).catch(err => {
-      console.log('checkPermission2Geo-error', err);
+          } else {
+            this.noPermissionSite();
+          }
+        }
+      })
+    }
+  }
+
+  noPermissionSite() {
+    console.log("未获取权限也获取站点列表");
+    this.setState({
+      hasPermission: false,
+      permissionDenied: true
     });
+    let first = this.state.stopList.length == 0;//是否第一次加载
+    this.getStationList(this.state.region, first);
   }
 
   /**
    * 获取定位数据
-   * @param {*} again
+   * @param {*} again 
    */
   infoGeoLocation(again) {
     if (this.state.mapReady) {
@@ -244,8 +234,8 @@ export default class HomePage extends Component {
         //console.log("getGeoLocation", region);
         //if (this.state.stopList.length == 0) { //只加载一次
         let time = new Date().getTime();
-        let interval = this.settingInfo.refreshInterval * 1000;
-        let first = this.state.stopList.length == 0;
+        let interval = this.settingInfo.refreshInterval * 1000;//重复加载时间
+        let first = this.state.stopList.length == 0;//是否第一次加载
         if (first || time - this.refreshTime > interval) { //一分钟(10秒)加载一次
           this.getStationList(region, first);
           this.refreshTime = time;
@@ -277,7 +267,10 @@ export default class HomePage extends Component {
    */
   getStationList(region, first) {
     if (!isIOS)
-      Dialog.showProgressDialog('Loading...');
+      Dialog.showProgressDialog();
+    if (getUserId()) {
+      this.filter.operaUserId = getUserId()
+    }
     apiStation.getAllStation(this.filter).then(res => {
       if (!isIOS)
         Dialog.dismissLoading();
@@ -296,6 +289,7 @@ export default class HomePage extends Component {
             siteType: item.siteType,
             latitude: item.locationLatitude,
             longitude: item.locationLongitude,
+            favorite: item.favorite,
             /*acConnector: item.acConnector,
             allConnector: item.allConnector,
             dcConnector: item.dcConnector,
@@ -305,6 +299,7 @@ export default class HomePage extends Component {
         this.setState({
           stopList: list
         });
+        //this.viewChargeStation(list[0].id) //测试显示站点
         if (region && (this.settingInfo.alwaysLocation || first)) {
           setTimeout(() => {
             this.setState({
@@ -324,33 +319,89 @@ export default class HomePage extends Component {
   }
 
   //点击Marker获取StaionInfo
-  viewChargeStation(id, index) {
+  viewChargeStation(id) {
     //console.log('info', this.state.stopList[index]);
     //const stationInfo = this.state.stopList[index];
-    navigator.geolocation.getCurrentPosition(location => {
-      let params = {
-        lat: location.coords.latitude,
-        lng: location.coords.longitude,
-        sitePk: id//stationInfo.id
+    if (app.modules.bookmarks) {
+      for (let i = 0; i < this.state.stopList.length; i++) {
+        let info = this.state.stopList[i];
+        if (info.id == id) {
+          this.viewIndex = i;
+          break;
+        }
+      }
+    }
+    if (this.state.hasPermission) {
+      navigator.geolocation.getCurrentPosition(location => {
+        let region = {
+          latitude: location.coords.latitude,
+          longitude: location.coords.longitude,
+          latitudeDelta: 0.0922,
+          longitudeDelta: 0.0421
+        }
+        this.getStationInfo(id, region);
+        /*if (this.settingInfo.alwaysLocation) {
+          this.setState({
+            region: region
+          });
+        }*/
+      });
+    } else {
+      //无定位权限仍然显示
+      this.getStationInfo(id, this.state.region);
+    }
+    //startPage(PageList.chargeDetail, {...this.state.stopList[index]});
+  }
+
+  getStationInfo(id, location) {
+    apiStation.getStationRate({
+      sitePk: id,
+      lat: location?.latitude,
+      lng: location?.longitude
+    }).then(res => {
+      if (res.data.sitePk) {
+        var info = utils.getSiteInfo(res.data);
+        this.setState({
+          stationInfo: info
+        });
+      } else {
+        this.setState({
+          stationInfo: {}
+        });
       }
-      apiStation.getStationRate(params).then(res => {
-        if (res.data.sitePk) {
-          var info = utils.getSiteInfo(res.data);
+    }).catch(err => {
+      this.setState({
+        stationInfo: {}
+      });
+      toastShort(err);
+    });
+  }
+
+  favoriteSite() {
+    if (this.state.stationInfo?.id) {
+      Dialog.showProgressDialog();
+      apiStation.bookmarkSite(this.state.stationInfo.id).then(res => {
+        const info = {...this.state.stationInfo, favorite: !this.state.stationInfo.favorite}
+        if (this.viewIndex >= 0) {
+          const list = [...this.state.stopList]
+          const inf = {...list[this.viewIndex], favorite: info.favorite};
+          list[this.viewIndex] = inf;
           this.setState({
+            stopList: list,
             stationInfo: info
           });
         } else {
           this.setState({
-            stationInfo: stationInfo
+            stationInfo: info
           });
         }
       }).catch(err => {
-        this.setState({
-          stationInfo: stationInfo
-        });
-      });
-    });
-    //startPage(PageList.chargeDetail, {...this.state.stopList[index]});
+        toastShort(err);
+        console.log(err)
+      }).finally(() => {
+        Dialog.dismissLoading();
+      })
+    }
   }
 
   render() {
@@ -369,6 +420,7 @@ export default class HomePage extends Component {
             <Image
               source={require('../../images/tool-logo.png')}
               style={Styles.logo}
+              resizeMode="contain"
             />
           </View>
           {/* <Text style={ui.flex1}></Text> */}
@@ -388,23 +440,28 @@ export default class HomePage extends Component {
           mapReady={this.state.mapReady}
           onFilter={data => this.findFilter(data)}
           onLocation={() => this.checkPermission2Geo(true)}
+          navigation={this.props.navigation}
         />
         <View style={styles.mapContent}>
-          { this.state.hasPermission &&
-            <Maps.Maps3
-              region={this.state.region}
-              stopList={this.state.stopList}
-              onMapReady={() => {
-                this.setState({
-                  mapReady: true
-                }, () => {
-                  this.checkPermission2Geo();
-                });
-              }}
-              onMarkerPress={id => this.viewChargeStation(id)}
-            />
-          }
-          <BottomSiteInfo stationInfo={this.state.stationInfo}/>
+          {/* this.state.hasPermission &&*/}
+          <Maps.Maps3
+            region={this.state.region}
+            stopList={this.state.stopList}
+            onMapReady={() => {
+              this.setState({
+                mapReady: true
+              }, () => {
+                this.checkPermission2Geo();
+              });
+            }}
+            onMarkerPress={id => this.viewChargeStation(id)}
+            showUserLocation={this.state.hasPermission && this.settingInfo.alwaysLocation}
+          />
+          <BottomSiteInfo
+            stationInfo={this.state.stationInfo}
+            onFavorite={() => this.favoriteSite()}
+          />
+          <LocationPermission.VIEW visible={this.state.permissionDenied}/>
         </View>
         <View style={styles.drawerLeftTouchView}></View>
       </View>

+ 42 - 13
Strides-APP/app/pages/home/maps/BottomSiteInfo.js

@@ -9,8 +9,9 @@ import utils from '../../../utils/utils';
 import { ChargeStyle } from '../../charge/Charging';
 import { PageList } from '../../Router';
 import ConnectType from '../../search/ConnectType';
+import app from '../../../../app.json';
 
-export default BottomSiteInfo = ({stationInfo = {}}) => {
+export default BottomSiteInfo = ({stationInfo = {}, onFavorite}) => {
   const getAvailable = (type) => {
     const all = stationInfo.allConnector;
     if (all) {
@@ -30,17 +31,17 @@ export default BottomSiteInfo = ({stationInfo = {}}) => {
     } else if (stationInfo.operatingHours) {
       return stationInfo.operatingHours;
     } else {
-      return 'To be updated';
+      return $t('charging.toBeUpdated');
     }
   }
 
   const getParkingFee = () => {
     if (stationInfo.parkingFeeFree) {
-      return "Free";
+      return $t('charging.free');
     } else if (stationInfo.parkingFee) {
       return stationInfo.parkingFee;
     } else {
-      return 'To be updated';
+      return $t('charging.toBeUpdated');
     }
   }
 
@@ -77,16 +78,32 @@ export default BottomSiteInfo = ({stationInfo = {}}) => {
               source={require('../../../images/charge/icon-type-interfaces.png')}/>
             <Text style={styles.availableText}>{getAvailable('inc')}</Text>
           </View> */}
+          { app.modules.bookmarks &&
+            <Pressable
+              style={[styles.directIconView, {backgroundColor: stationInfo.favorite ? colorPrimary : colorCancel}]}
+              android_ripple={rippleLess}
+              onPress={onFavorite}>
+              <MaterialIcons
+                name="star"
+                size={26}
+                color={colorLight}/>
+            </Pressable>
+          }
           <Pressable
-            style={styles.directView}
+            style={styles.directIconView}
+            android_ripple={rippleLess}
             onPress={() => {
               utils.directMaps(stationInfo.latitude, stationInfo.longitude, stationInfo.address);
             }}>
-            <MaterialIcons
+            <MaterialCommunityIcons
+              name="navigation-variant"
+              size={25}
+              color={colorLight}/>
+            {/* <MaterialIcons
               name='directions'
               size={28}
               color={colorAccent}/>
-            <Text style={styles.distanceText}>{stationInfo.distance}</Text>
+            <Text style={styles.distanceText}>{stationInfo.distance}</Text> */}
           </Pressable>
         </View>
         <View style={ui.flex}>
@@ -96,7 +113,7 @@ export default BottomSiteInfo = ({stationInfo = {}}) => {
           </View>
           <View style={styles.stationStatusView}>
             { (stationInfo.allConnector && stationInfo.allConnector.available > 0) &&
-              <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable, styles.stationStatus]}>Available</Text>
+              <Text style={[ChargeStyle.infoStatus, ChargeStyle.statusAvailable, styles.stationStatus]}>{$t("charging.statusAvailable")}</Text>
             }
             <Text style={[ChargeStyle.infoStatus, stationInfo.siteType == 'Public' ? ChargeStyle.statusAvailable : ChargeStyle.statusWarning, styles.siteTypes]}>{stationInfo.siteType}</Text>
           </View>
@@ -106,21 +123,21 @@ export default BottomSiteInfo = ({stationInfo = {}}) => {
           style={styles.infoDetailsView}
           onPress={toChargePage}>
           <View style={ui.flex1}>
-            <Text style={styles.infoTitle}>Operating Hours</Text>
+            <Text style={styles.infoTitle}>{$t("charging.operatingHours")}</Text>
             <View style={styles.infoView}>
               <Text style={styles.infoText}>{getOperatingHours()}</Text>
             </View>
           </View>
           <View style={{width: 4}}></View>
           <View style={ui.flex1}>
-            <Text style={styles.infoTitle}>Parking Charges</Text>
+            <Text style={styles.infoTitle}>{$t("charging.parkingFees")}</Text>
             <View style={styles.infoView}>
               <Text style={styles.infoText}>{getParkingFee()}</Text>
             </View>
           </View>
           <View style={{width: 4}}></View>
           <View style={ui.flex1}>
-            <Text style={styles.infoTitle}>Additional Info</Text>
+            <Text style={styles.infoTitle}>{$t("charging.additionalInfo")}</Text>
             <View style={styles.infoView}>
               <Text style={styles.infoText}>{stationInfo?.additionalNotes}</Text>
             </View>
@@ -143,7 +160,7 @@ const styles = StyleSheet.create({
     ...$padding(12, 9),
     position: 'absolute',
     backgroundColor: colorLight,
-    ...ElevationObject(1.5)
+    ...ElevationObject(3)
   },
   stationInfo: {
     flex: 1,
@@ -155,7 +172,7 @@ const styles = StyleSheet.create({
   stationTitle: {
     color: '#000',
     fontSize: 16,
-    paddingBottom: 2
+    paddingBottom: 4
   },
   stationAddress: {
     color: '#999',
@@ -204,6 +221,18 @@ const styles = StyleSheet.create({
     color: textPrimary,
     fontSize: 12,
   },
+  directIconView: {
+    zIndex: 1,
+    width: 42,
+    height: 42,
+    marginLeft: 8,
+    marginRight: 8,
+    borderRadius: 45,
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: colorAccent,
+    ...ElevationObject(2)
+  },
   connectView: {
     flex: 1,
     paddingTop: 12,

+ 16 - 8
Strides-APP/app/pages/home/maps/Cluster.js

@@ -13,13 +13,21 @@ export const MyMarker = ({id, coordinate, onPress}) => {
       key={id}
       coordinate={coordinate}
       onPress={onPress}>
-      { coordinate.available
-        ? <Image
-            style={styles.marker}
-            source={require('../../../images/maps/ic_marker.png')}/>
-        : <Image
-            style={styles.marker}
-            source={require('../../../images/maps/ic_marker_un.png')}/>
+      { coordinate.favorite
+        ? coordinate.available
+          ? <Image
+              style={styles.marker}
+              source={require('../../../images/maps/ic_marker_star.png')}/>
+          : <Image
+              style={styles.marker}
+              source={require('../../../images/maps/ic_marker_unstar.png')}/>
+        : coordinate.available
+          ? <Image
+              style={styles.marker}
+              source={require('../../../images/maps/ic_marker.png')}/>
+          : <Image
+              style={styles.marker}
+              source={require('../../../images/maps/ic_marker_un.png')}/>
       }
     </Marker>
   )
@@ -81,7 +89,7 @@ const styles = StyleSheet.create({
     height: 39.6
   },
   textAvailable: {
-    color: 'white',
+    color: textLight,
     fontSize: 16,
     paddingBottom: 7
   },

+ 145 - 0
Strides-APP/app/pages/home/maps/LocationPermission.js

@@ -0,0 +1,145 @@
+/**
+ * 获取位置权限处理
+ * @邠心vbe on 2023/02/15
+ */
+import React from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+import { check, openSettings, PERMISSIONS, request, RESULTS } from 'react-native-permissions';
+import Button from '../../../components/Button';
+
+//global.hasPermission = false;
+
+/**
+ * 检查是否有定位权限
+ * @param {function} back callback(hasPermission, canRequestPermission)
+ */
+const checkPermission = (back) => {
+  check(
+      isIOS 
+    ? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
+    : PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION)
+  .then(res => {
+    console.log('[LocationPermission] checkPermission', res);
+    switch (res) {
+      case RESULTS.UNAVAILABLE:
+        console.log('此功能不可用(在此设备上/在此上下文中)');
+        if (isIOS) {
+          back(true, true);
+        } else {
+          back(false, true);
+        }
+        break;
+      case RESULTS.DENIED:
+        console.log('权限未被请求/被拒绝,但可以请求');
+        back(false, true);
+        break;
+      case RESULTS.LIMITED:
+        console.log('权限是有限的:有些操作是可能的');
+        back(true, true);
+        break;
+      case RESULTS.GRANTED:
+        console.log('许可被授予');
+        back(true, true);
+        break;
+      case RESULTS.BLOCKED:
+        console.log('权限被拒绝,不再可请求');
+        back(false, false);
+        /*Dialog.showDialog({
+          title: 'Error',
+          message: 'Can not get charge station, Please grant location permissions.',
+          ok: 'SETTING',
+          callback: btn => {
+            if (btn == Dialog.BUTTON_OK) {
+              console.log('ok');
+              openSettings().catch(() => console.warn('cannot open settings'));
+            }
+          }
+        });*/
+        break;
+    }
+  }).catch(erros => {
+    console.log('[LocationPermission] checkPermission-catch', erros);
+    back(false, false);
+  });
+}
+
+/**
+ * 请求定位权限
+ * @param {function} back callback(hasPermission)
+ */
+const getPermission = (back) => {
+  request(
+      isIOS 
+    ? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
+    : PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION)
+  .then(res => {
+    console.log('[LocationPermission] requestPermission', res);
+    back(res == RESULTS.GRANTED);
+  }).catch(erros => {
+    console.info('[LocationPermission] requestPermission-catch', erros)
+    back(false);
+  });
+}
+
+const LocationPermission = ({visible=false}) => (
+  visible
+  ? <View style={styles.permissionView}>
+      <Text
+        style={styles.permissionText}
+        numberOfLines={1}>{$t("home.locationPermissionTips")}</Text>
+      <Button
+        style={styles.viewButton}
+        viewStyle={styles.viewButtonStyle}
+        onClick={() => openSettings().catch(() => console.warn('cannot open settings'))}>
+        <Text style={styles.buttonText}>{$t("nav.view")}</Text>
+        <FontAwesome
+          size={16}
+          color={colorPrimary}
+          name='angle-right'/>
+      </Button>
+    </View>
+  : <></>
+);
+
+const styles = StyleSheet.create({
+  permissionView: {
+    //top: 80,
+    left: 16,
+    right: 16,
+    bottom: 32,
+    opacity: .8,
+    overflow: 'hidden',
+    borderRadius: 30,
+    position: 'absolute',
+    alignItems: 'center',
+    flexDirection: 'row',
+    backgroundColor: '#FEB751'
+  },
+  permissionText: {
+    flex: 1,
+    color: textDark,
+    fontSize: 11,
+    paddingLeft: 16,
+    paddingRight: 2
+  },
+  viewButton: {
+    borderRadius: 0,
+    backgroundColor: 'transparent'
+  },
+  viewButtonStyle: {
+    alignItems: 'center',
+    flexDirection: 'row',
+    ...$padding(8, 12, 8, 8),
+  },
+  buttonText: {
+    color: colorPrimary,
+    fontSize: 11,
+    paddingRight: 4
+  }
+})
+
+export default {
+  VIEW: LocationPermission,
+  checkPermission: checkPermission,
+  requestPermission: getPermission
+}

+ 129 - 0
Strides-APP/app/pages/payment/PaymentConfig.js

@@ -0,0 +1,129 @@
+import React from 'react';
+import { Image } from 'react-native';
+import Svg, { Path } from 'react-native-svg';
+import { PageList } from '../Router';
+
+export const PAYTYPE = {
+  CREDIT_WALLET: "CreditWallet", // 余额支付
+  PAY_PER_USE: "PayPerUse" // 按次支付
+}
+
+/**
+ * @is2c2p 控制是否启用2C2P
+ * @DEFAULT 控制默认的支付方式
+ */
+ export const PaymentDefault = {
+  is2c2p: true, //是否启用2C2P
+  enablePayPerUse: false, //是否启用按次支付
+  DEFAULT: {
+    payType: PAYTYPE.CREDIT_WALLET,
+    payName: "Credit Wallet",
+    isWallet: true,
+    isPayPerUse: false
+  },
+  DEFAULT2: {
+    payType: PAYTYPE.PAY_PER_USE,
+    payName: "Pay Per Use (SGQR)",
+    isWallet: false,
+    isPayPerUse: true
+  }
+}
+
+export const getPaymenOptions = (back) => {
+  if (PaymentDefault.enablePayPerUse) {
+    if (PaymentDefault.DEFAULT.payType == PAYTYPE.PAY_PER_USE) {
+      back([{
+        title: $t('payment.payPerUse'),
+        desc: '(SGQR)',
+        value: PAYTYPE.PAY_PER_USE,
+        iconFont: "PAYNOW",
+        icon: require('../../images/wallet/ic_payperuse.png')
+      }, {
+        title:  $t('wallet.creditWallet'),
+        desc: '',
+        value: PAYTYPE.CREDIT_WALLET,
+        iconFont: "WALLET",
+        icon: require('../../images/icon/draw-wallet.png')
+      }])
+    } else {
+      back([{
+        title: $t('wallet.creditWallet'),
+        desc: '',
+        value: PAYTYPE.CREDIT_WALLET,
+        iconFont: "WALLET",
+        icon: require('../../images/icon/draw-wallet.png')
+      }, {
+        title: $t('payment.payPerUse'),
+        desc: '(SGQR)',
+        value: PAYTYPE.PAY_PER_USE,
+        iconFont: "PAYNOW",
+        icon: require('../../images/wallet/ic_payperuse.png')
+      }])
+    }
+  } else {
+    back([{
+      title: $t('wallet.creditWallet'),
+      desc: '',
+      value: PAYTYPE.CREDIT_WALLET,
+      iconFont: "WALLET",
+      icon: require('../../images/icon/draw-wallet.png')
+    }])
+  }
+}
+
+export const PaymentIcon = ({method="CARD", size=40, checked=false}) => {
+  switch (method) {
+    case "CARD": 
+      return (
+        /*<Svg width="40" height="40" viewBox="0 0 40 40">
+          <Path
+            fill={checked ? colorAccent : "#666666"}
+            d="M22.0532 16.1052C21.3736 16.8184 21 17.7567 21 18.7535V20.2465C21 21.2433 21.3736 22.1817 22.0532 22.8948C22.7327 23.608 23.6269 24 24.5768 24H37V15H24.5768C23.6269 15 22.7288 15.392 22.0532 16.1052ZM26.0392 17.8318C26.9175 17.8318 27.6289 18.5783 27.6289 19.5C27.6289 20.4217 26.9175 21.1682 26.0392 21.1682C25.161 21.1682 24.4496 20.4217 24.4496 19.5C24.4496 18.5783 25.161 17.8318 26.0392 17.8318Z"/>
+          <Path
+            fill={checked ? colorAccent : "#666666"}
+            d="M32.2342 4H6.7697C4.1403 4 2 6.16334 2 8.82988V30.1701C2 32.8327 4.13639 35 6.7697 35H32.2303C34.8597 35 37 32.8367 37 30.1701V25.7563H24.7686C21.7557 25.7563 19.2907 23.2601 19.2907 20.2092V18.7908C19.2907 15.7399 21.7557 13.2437 24.7686 13.2437H37V8.82988C37 6.16731 34.8597 4 32.2342 4Z" />
+        </Svg>*/
+        <MaterialCommunityIcons
+          name="credit-card-multiple"
+          color={checked ? colorAccent : "#666666"}
+          size={size}/>
+      );
+    case "PAYNOW":
+      return (
+        <Svg width={size} height={size} viewBox="0 0 40 40">
+          <Path
+            fill={checked ? colorAccent : "#666666"}
+            d="M4 18.2222H7.55556V21.7778H4V18.2222ZM18.2222 7.55556H21.7778V14.6667H18.2222V7.55556ZM14.6667 18.2222H21.7778V25.3333H18.2222V21.7778H14.6667V18.2222ZM25.3333 18.2222H28.8889V21.7778H32.4444V18.2222H36V21.7778H32.4444V25.3333H36V32.4444H32.4444V36H28.8889V32.4444H21.7778V36H18.2222V28.8889H25.3333V25.3333H28.8889V21.7778H25.3333V18.2222ZM32.4444 32.4444V25.3333H28.8889V32.4444H32.4444ZM25.3333 4H36V14.6667H25.3333V4ZM28.8889 7.55556V11.1111H32.4444V7.55556H28.8889ZM4 4H14.6667V14.6667H4V4ZM7.55556 7.55556V11.1111H11.1111V7.55556H7.55556ZM4 25.3333H14.6667V36H4V25.3333ZM7.55556 28.8889V32.4444H11.1111V28.8889H7.55556Z"/>
+        </Svg>
+      )
+    case "GRABPAY":
+      return (
+        <Image
+          style={{width: size, height: size}}
+          source={checked ? require('../../images/wallet/payment-grab-active.png') : require('../../images/wallet/payment-grab.png')}
+        />
+      );
+    case "WALLET":
+      return (
+        <Svg width={size} height={size} viewBox="0 0 40 40">
+          <Path
+            fill={checked ? colorAccent : "#666666"}
+            d="M22.0532 16.1052C21.3736 16.8184 21 17.7567 21 18.7535V20.2465C21 21.2433 21.3736 22.1817 22.0532 22.8948C22.7327 23.608 23.6269 24 24.5768 24H37V15H24.5768C23.6269 15 22.7288 15.392 22.0532 16.1052ZM26.0392 17.8318C26.9175 17.8318 27.6289 18.5783 27.6289 19.5C27.6289 20.4217 26.9175 21.1682 26.0392 21.1682C25.161 21.1682 24.4496 20.4217 24.4496 19.5C24.4496 18.5783 25.161 17.8318 26.0392 17.8318Z"/>
+          <Path
+            fill={checked ? colorAccent : "#666666"}
+            d="M32.2342 4H6.7697C4.1403 4 2 6.16334 2 8.82988V30.1701C2 32.8327 4.13639 35 6.7697 35H32.2303C34.8597 35 37 32.8367 37 30.1701V25.7563H24.7686C21.7557 25.7563 19.2907 23.2601 19.2907 20.2092V18.7908C19.2907 15.7399 21.7557 13.2437 24.7686 13.2437H37V8.82988C37 6.16731 34.8597 4 32.2342 4Z"/>
+        </Svg>
+      )
+  }
+}
+
+/*export const toTopupPage = () => {
+  if (PaymentDefault.is2c2p) {
+    startPage(PageList.topupV2);
+  } else {
+    startPage(PageList.topup);
+  }
+}*/
+export const toTopupPage = () => {
+  startPage(PageList.topupNew);
+}

+ 65 - 27
Strides-APP/app/pages/search/ListViewV2.js

@@ -8,8 +8,9 @@ import TextRadius from '../../components/TextRadius';
 import utils from '../../utils/utils';
 import Provider from '../charge/Provider';
 import ConnectType from './ConnectType';
+import app from '../../../app.json';
 
-export default ListViewV2 = ({item, index, separators, onPress}) => {
+export default ListViewV2 = ({item, index, separators, onPress, onFavorite}) => {
   if (item.id) {
     return (
       <Pressable 
@@ -17,39 +18,65 @@ export default ListViewV2 = ({item, index, separators, onPress}) => {
         key={index}
         onPress={onPress}
         android_ripple={ripple}>
-        <Ionicons
+        {/* <Ionicons
           name="md-location-sharp"
           size={20}
           color="#E5E5E5"
-        />
+        /> */}
         <View style={styles.stationInfo} >
           <Text style={styles.stationName}>{item.name}</Text>
-          <Text style={styles.stationAddress}>{item.address}</Text>
-          {/* <Provider providers={item.serviceProvider}/> */}
-          <View style={styles.connectView}>
-            <ConnectType color={textCancel} {...item.acConnector}/>
-            <ConnectType color={textCancel} {...item.dcConnector}/>
-          </View>
-          <View style={ui.flexc}>
-            <TextRadius style={[styles.infoStatus, styles.available]}>{item.distance}</TextRadius>
-            {item.allConnector && item.allConnector.available > 0 &&
-              <TextRadius style={[styles.infoStatus, styles.available]}>Available</TextRadius>
-            }
-            {item.siteType == "Private" &&
-              <TextRadius style={[styles.infoStatus, styles.private]}>Private Site</TextRadius>
+          <View style={ui.flex}>
+            <View style={ui.flex1}>
+              <Text style={styles.stationAddress}>{item.address}</Text>
+              {/* <Provider providers={item.serviceProvider}/> */}
+              <View style={styles.connectView}>
+                <ConnectType color={textCancel} {...item.acConnector}/>
+                <ConnectType color={textCancel} {...item.dcConnector}/>
+              </View>
+              <View style={ui.flexc}>
+                <TextRadius style={[styles.infoStatus, styles.available]}>{item.distance}</TextRadius>
+                {item.allConnector && item.allConnector.available > 0 &&
+                  <TextRadius style={[styles.infoStatus, styles.available]}>{$t('charging.statusAvailable')}</TextRadius>
+                }
+                {item.siteType == "Private" &&
+                  <TextRadius style={[styles.infoStatus, styles.private]}>{$t('home.statusPrivate')}</TextRadius>
+                }
+              </View>
+            </View>
+            { app.modules.bookmarks &&
+              <Pressable
+                style={[styles.directIconView, {backgroundColor: item.favorite ? colorPrimary : colorCancel}]}
+                android_ripple={rippleLess}
+                onPress={onFavorite}>
+                <MaterialIcons
+                  name="star"
+                  size={22}
+                  color={colorLight}/>
+              </Pressable>
             }
+            <Pressable
+              style={styles.directIconView}
+              android_ripple={rippleLess}
+              onPress={() => {
+                utils.directMaps(item.latitude, item.longitude, item.address);
+              }}>
+              <MaterialCommunityIcons
+                name="navigation-variant"
+                size={22}
+                color={colorLight}/>
+            </Pressable>
+            {/* <Pressable 
+              style={styles.directView}
+              onPress={() => {
+                utils.directMaps(item.latitude, item.longitude, item.address);
+              }}>
+              <MaterialIcons
+                name='directions'
+                size={32}
+                color={colorAccent}/>
+            </Pressable> */}
           </View>
         </View>
-        <Pressable 
-          style={styles.directView}
-          onPress={() => {
-            utils.directMaps(item.latitude, item.longitude, item.address);
-          }}>
-          <MaterialIcons
-            name='directions'
-            size={32}
-            color={colorAccent}/>
-        </Pressable>
       </Pressable>
     );
   } else {
@@ -66,7 +93,7 @@ const styles = StyleSheet.create({
   },
   stationInfo: {
     flex: 1,
-    paddingLeft: 8
+    paddingLeft: 0
   },
   nameView: {
     paddingTop: 3,
@@ -139,4 +166,15 @@ const styles = StyleSheet.create({
     fontSize: 12,
     paddingTop: 2
   },
+  directIconView: {
+    zIndex: 1,
+    width: 32,
+    height: 32,
+    marginTop: 4,
+    marginLeft: 16,
+    borderRadius: 45,
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: colorAccent
+  },
 })

+ 35 - 10
Strides-APP/app/pages/search/SearchV2.js

@@ -5,11 +5,12 @@
 import React, { Component } from 'react';
 import { View, Text, StyleSheet, TextInput, FlatList, Image } from 'react-native';
 import apiStation from '../../api/apiStation';
+import Dialog from '../../components/Dialog';
 import utils from '../../utils/utils';
 import { PageList } from '../Router';
 import ListViewV2 from './ListViewV2';
 
-export default class SearchV2 extends Component {
+export default class Search extends Component {
   constructor(props) {
     super(props);
     this.state = {
@@ -37,12 +38,12 @@ export default class SearchV2 extends Component {
       })
       this.searchStation(latlng);
     }, error => {
-      console.warn("getGeoLocation", error);
+      console.info("[Search] getGeoLocation", error);
       this.searchStation(this.state.latlng);
     });
   }
 
-  searchStation(latlng, time=1500) {
+  searchStation(latlng, time=1000) {
     this.setState({
       isSearch: true
     });
@@ -61,6 +62,7 @@ export default class SearchV2 extends Component {
             allConnector: item.allConnector,
             dcConnector: item.dcConnector,
             siteType: item.siteType,
+            favorite: item.favorite,
             distance: utils.getDistance(item.distance),
             serviceProvider: item.serviceProvider
           });
@@ -73,7 +75,7 @@ export default class SearchV2 extends Component {
         }, time);
       }
     }).catch(err => {
-      console.log('err', err);
+      console.log('searchStation-err', err);
       this.setState({
         isSearch: false,
         searchResult: []
@@ -81,12 +83,36 @@ export default class SearchV2 extends Component {
     });
   }
 
+  favoriteSite(index, info) {
+    if (info?.id) {
+      Dialog.showProgressDialog();
+      apiStation.bookmarkSite(info.id).then(res => {
+        if (index >= 0) {
+          const list = [...this.state.searchResult];
+          list[index].favorite = !info.favorite;
+          this.setState({
+            searchResult: list
+          });
+        }
+      }).catch(err => {
+        toastShort(err);
+      }).finally(() => {
+        Dialog.dismissLoading();
+      })
+    }
+  }
+
   intoStation(info) {
     startPage(PageList.chargeDetailPage, {stationInfo: info, action: 'search', from: PageList.search});
   }
 
   listItem = (props) => {
-    return <ListViewV2 {...props} onPress={() => this.intoStation(props.item)}/>
+    return (
+      <ListViewV2 
+        {...props}
+        onPress={() => this.intoStation(props.item)}
+        onFavorite={() => this.favoriteSite(props.index, props.item)}/>
+    )
   }
 
   render() {
@@ -104,13 +130,14 @@ export default class SearchV2 extends Component {
             numberOfLines={1}
             returnKeyType={'search'}
             clearButtonMode={'while-editing'}
-            placeholder='Search using site name or service provider'
+            placeholder={$t('home.searchHint')}
             placeholderTextColor={textPlacehoder}
             onChangeText={text => {
               this.searchWorld = text;
             }}
             onSubmitEditing={() => {
-              this.searchStation(this.state.latlng, 1000);
+              //this.getGeoLocation();
+              this.searchStation(this.state.latlng, 500);
             }}/>
         </View>
         { this.state.isSearch
@@ -125,7 +152,7 @@ export default class SearchV2 extends Component {
             renderItem={this.listItem}
             keyExtractor={item => item.id}
             keyboardShouldPersistTaps="always"
-            ListEmptyComponent={<Text style={styles.noResult}>No search result</Text>}
+            ListEmptyComponent={<Text style={styles.noResult}>{$t('home.noSearch')}</Text>}
           />
         }
       </View>
@@ -234,8 +261,6 @@ const styles = StyleSheet.create({
     alignItems: 'center',
     flexDirection: 'row'
   },
-
-  
   directView: {
     zIndex: 1,
     paddingTop: 4,

+ 1 - 1
Strides-APP/app/pages/wallet/Wallet.js

@@ -84,7 +84,7 @@ export default class Wallet extends Component {
         style={styles.container}
         refreshControl={
           <RefreshControl
-            {...MyRefreshProps}
+            {...MyRefreshProps()}
             refreshing={this.state.refreshing}
             onRefresh={() => this.onRefresh()}
           />

+ 66 - 8
Strides-APP/app/utils/utils.js

@@ -30,13 +30,13 @@ export default {
         latitude: lat,
         longitude: lng,
         title: address
-      }).catch(err => {
-        console.warn("directMaps", err);
+      }).catch(erros => {
+        console.warn("directMaps", erros);
       });
     } else {
       var uri = "geo:" + lat +  "," + lng + "?q=" + address;
-      Linking.openURL(uri).catch(err => {
-        console.warn("directMaps", err);
+      Linking.openURL(uri).catch(erros => {
+        console.warn("directMaps", erros);
       });
     }
   },
@@ -61,6 +61,7 @@ export default {
         allConnector: obj.allConnector,
         dcConnector: obj.dcConnector,
         distance: this.getDistance(obj.distance),
+        favorite: obj.favorite,
         acRates: acRates,
         dcRates: dcRates,
         rateList: obj.rates,
@@ -119,14 +120,28 @@ export default {
       if (m > 60) {
         const h = m / 60;
         const mm = m % 60;
-        return parseInt(h) + ' hr ' + parseInt(mm) + 'min';
+        return parseInt(h) + ' hr ' + parseInt(mm) + ' min';
       } else {
-        return parseInt(minutes) + 'min';
+        return parseInt(minutes) + ' min';
       }
     } else {
       return '0 min';
     }
   },
+  minutes2HHMM(minutes) {
+    if (minutes) {
+      const m = Number(minutes);
+      if (m > 60) {
+        const h = m / 60;
+        const mm = m % 60;
+        return formatNumber(parseInt(h)) + ':' + formatNumber(parseInt(mm));
+      } else {
+        return '00:' + formatNumber(parseInt(minutes));
+      }
+    } else {
+      return '00:00';
+    }
+  },
   isEmpty(str, encNo=false) {
     if (typeof str == 'number') {
       if (encNo) {
@@ -189,12 +204,55 @@ export default {
       return text;
     }
   },
+  /**
+   * 16进制颜色转为RGB
+   * @param {*} value 16进制颜色
+   * @returns RGB数据
+   */
+  hexColorToRgb(value) {
+    let sColor = value.toLowerCase()
+    if (sColor  && sColor.indexOf("#") >= 0) {
+      if (sColor.length === 4) {
+        let sColorNew = '#'
+        for (let i = 1; i < 4; i += 1) {
+          sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
+        }
+        sColor = sColorNew
+      }
+      // 处理1+6位的颜色值
+      let sColorChange = []
+      for (let i = 1; i < 7; i += 2) {
+        sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)))
+      }
+      return sColorChange;//'rgb(' + sColorChange.join(',') + ')'
+    } else if (sColor && sColor.indexOf("0x") >= 0) {
+      if (sColor.length === 4) {
+        let sColorNew = '0x'
+        for (let i = 1; i < 4; i += 1) {
+          sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
+        }
+        sColor = sColorNew
+      }
+      // 处理2+6位的颜色值
+      let sColorChange = []
+      for (let i = 2; i < 8; i += 2) {
+        sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)))
+      }
+      return sColorChange;//'rgb(' + sColorChange.join(',') + ')'
+    }
+    return sColor
+  },
+  getRgbaColor(colors, alpha=1) {
+    if (colors.length == 3) {
+      return "rgba(" + this.join(colors, ",") + "," +alpha + ")";
+    }
+  },
   /**
    * 注册FCM通知token
-   * @param {String} token Firebase令牌
+   * @param {String} token 原始保存的Firebase令牌
    * @param {Function} back 执行结果(boolean)
    */
-  async registerFirebaseToken(token, back) {
+   async registerFirebaseToken(token, back) {
     const thisDate = this.formatYYMM(new Date()) + "-" + getUserId();
     const lastDate = await getStorageSync('RegisterTokenDate');
     console.log('>>>RegisterToken<<<', thisDate, lastDate + "●");

+ 1 - 1
Strides-APP/app/utils/vector_icon.js

@@ -37,7 +37,7 @@ global.MaterialCommunityIcons = MaterialCommunityIcons;
 
 global.MaterialIcons = MaterialIcons;
 
-//global.Octicons = Octicons;
+global.Octicons = Octicons;
 //global.SimpleLineIcons = SimpleLineIcons;
 
 global.Zocial = Zocial;

+ 66 - 85
Strides-APP/index.js

@@ -1,8 +1,10 @@
 /**
- * @format
+ * 入口文件
+ * @邠心vbe on 2020/05/20
  */
-import React, { useEffect } from 'react';
-import {AppRegistry, KeyboardAvoidingView, StatusBar} from 'react-native';
+import React, { Component } from 'react';
+import './app/i18n'
+import {AppRegistry, KeyboardAvoidingView} from 'react-native';
 import codePush from "react-native-code-push";
 import 'react-native-gesture-handler';
 import './app/utils/themes'
@@ -15,92 +17,71 @@ import app from './app.json';
 import ModalPortal from './app/components/ModalPortal';
 import {RootSiblingParent} from 'react-native-root-siblings';
 import { SafeAreaView } from 'react-native-safe-area-context';
+import MyStatusBar from './app/components/MyStatusBar';
+import { i18nUtil } from './app/i18n';
 
-const Index = () => {
-  useEffect(() => {
-    //Analytics.trackEvent("IntoApp", {versionName: app.versionName, versionCode: app.versionCode});
-    if (isIOS) {
-      checkUpdate();
+//let context;
+class Index extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      visible: false,
+      refreshTime: 0
     }
-  })
-  return (
-    <RootSiblingParent>
-      <StatusBar barStyle={themeStatusBar} backgroundColor={colorPrimaryDark}/>
-      { isIOS
-        ? <KeyboardAvoidingView style={ui.flex1} behavior="padding">
-            <Router />
-          </KeyboardAvoidingView>
-        : <Router />
-      }
-      <ModalPortal />
-      <SafeAreaView style={{flex: 0, backgroundColor: colorLight}}></SafeAreaView>
-    </RootSiblingParent>
-  );
-};
+    //context = this;
+  }
 
-const checkUpdate = () => {
-  console.log("[CodePush-iOS]", "Checking Update");
-  //Analytics.trackEvent('checkUpdate', { versionName: app.versionName, versionCode: app.versionCode });
-  //codePush.disallowRestart();
-  codePush.sync({
-    updateDialog: app.debug,
-    installMode: codePush.InstallMode.IMMEDIATE
-  }, status => {
-    //Analytics.trackEvent('checkUpdate-status', status);
-    switch(status) {
-      case codePush.SyncStatus.DOWNLOADING_PACKAGE:
-        if (app.debug) {
-          toastShort("Downloading update...")
-        }
-        console.log("[CodePush-iOS]", "Downloading update");
-        break;
-      case codePush.SyncStatus.INSTALLING_UPDATE:
-        if (app.debug) {
-          toastShort("Installing update")
-        }
-        console.log("[CodePush-iOS]", "Installing update");
-        break;
-      case codePush.SyncStatus.UP_TO_DATE:
-        if (app.debug) {
-          toastShort("App is up to date")
-        }
-        console.log("[CodePush-iOS]", "App is up to date");
-        codePush.notifyAppReady();
-        break;
-      case codePush.SyncStatus.UPDATE_INSTALLED:
-        if (app.debug) {
-          toastShort("Update installed")
-        }
-        console.log("[CodePush-iOS]", "Update installed");
-        codePush.notifyAppReady();
-        //codePush.restartApp(true);
-        break;
-    }
-  }, process => {
-    
-  });
-}
+  componentDidMount() {
+    i18nUtil.init(() => {
+      this.setState({
+        visible: true
+      })
+    })
+  }
+
+  /*restartApp() {
+    console.log("----------------RESTART-----------------");
+    this.setState({
+      visible:false
+    }, () => {
+      this.setState({
+        visible: true
+      })
+    })
+  }*/
 
-//热更新配置
-let codePushIos = {
-  updateDialog: true,
-  //实时检测更新并下载
-  checkFrequency: codePush.CheckFrequency.MANUAL,
-  //下载完成后立即安装
-  //installMode: codePush.InstallMode.IMMEDIATE
-  //下次进入安装
-  //installMode: codePush.InstallMode.ON_NEXT_RESTART
+  render() {
+    return (
+      <RootSiblingParent>
+        <MyStatusBar/>
+        { this.state.visible 
+          ? isIOS
+            ? <KeyboardAvoidingView style={ui.flex1} behavior="padding">
+                <Router/>
+              </KeyboardAvoidingView>
+            : <Router/>
+          : <></>
+        }
+        <ModalPortal />
+        <SafeAreaView style={{flex: 0, backgroundColor: colorLight}}></SafeAreaView>
+      </RootSiblingParent>
+    );
+  }
 };
 
-let codePushAndroid = {
-  updateDialog: false,
-  //实时检测更新并下载
-  checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
-  //下载完成后立即安装
-  installMode: codePush.InstallMode.IMMEDIATE
-  //下次进入安装
-  //installMode: codePush.InstallMode.ON_NEXT_RESTART
-}
+if (app.codePush) {
+  //热更新配置
+  let codePushOptions = {
+    updateDialog: false,
+    //实时检测更新并下载
+    checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
+    //下载完成后立即安装
+    installMode: codePush.InstallMode.IMMEDIATE
+    //下次进入安装
+    //installMode: codePush.InstallMode.ON_NEXT_RESTART
+  };
 
-AppRegistry.registerComponent(app.name, () => codePush(isIOS ? codePushIos : codePushAndroid)(Index));
-//AppRegistry.registerComponent(appName, () => Index);
+  AppRegistry.registerComponent(app.name, () => codePush(codePushOptions)(Index));
+} else {
+  AppRegistry.registerComponent(app.name, () => Index);
+}

+ 1 - 0
Strides-APP/package.json

@@ -46,6 +46,7 @@
     "react-native-device-info": "^8.3.1",
     "react-native-gesture-handler": "^1.10.3",
     "react-native-gradients": "^1.1.1",
+    "react-native-i18n": "^2.0.15",
     "react-native-image-crop-picker": "^0.36.2",
     "react-native-map-clustering": "^3.4.2",
     "react-native-map-link": "^2.7.27",