Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
feat(i18n): detect best-fit user currency
Browse files Browse the repository at this point in the history
Use the locale information in order to select a best fit default
currency for the user.
  • Loading branch information
mrfelton committed Sep 19, 2018
1 parent cc2e789 commit 27e46cc
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 60 deletions.
4 changes: 2 additions & 2 deletions app/components/Settings/Locale/Locale.js
@@ -1,7 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import FaAngleLeft from 'react-icons/lib/fa/angle-left'
import ISO6391 from 'iso-639-1'
import { getLanguageName } from 'lib/utils/i18n'
import Isvg from 'react-inlinesvg'
import checkIcon from 'icons/check.svg'

Expand Down Expand Up @@ -29,7 +29,7 @@ const Translate = ({ locales, disableSubMenu, currentLocale, setLocale }) => {
className={currentLocale === lang ? styles.active : ''}
onClick={() => changeLocale(lang)}
>
<span>{ISO6391.getName(lang.split('-')[0])}</span>
<span>{getLanguageName(lang)}</span>
{currentLocale === lang && <Isvg src={checkIcon} />}
</li>
)
Expand Down
7 changes: 4 additions & 3 deletions app/index.js
Expand Up @@ -7,12 +7,13 @@ import Root from './containers/Root'
import { configureStore, history } from './store/configureStore'
import './styles/app.global.scss'

import { translationMessages, DEFAULT_LOCALE } from './lib/utils/i18n'
import { translationMessages, getLocale } from './lib/utils/i18n'

const locale = getLocale()
const initialState = {
intl: {
locale: DEFAULT_LOCALE,
messages: translationMessages[DEFAULT_LOCALE],
locale,
messages: translationMessages[locale],
timeZone: jstz.determine().name()
}
}
Expand Down
109 changes: 90 additions & 19 deletions app/lib/utils/i18n.js
@@ -1,6 +1,9 @@
import { app, remote } from 'electron'
import Store from 'electron-store'
import { addLocaleData } from 'react-intl'
import Store from 'electron-store'
import get from 'lodash.get'
import { lookup } from 'country-data-lookup'
import createDebug from 'debug'

// Load locale data.
import bg from 'react-intl/locale-data/bg'
Expand Down Expand Up @@ -41,6 +44,8 @@ import ukTranslationMessages from '../../translations/uk-UA.json'
import zhCNTranslationMessages from '../../translations/zh-CN.json'
import zhTWTranslationMessages from '../../translations/zh-TW.json'

const debug = createDebug('zap:i18n')

// Add locale data.
addLocaleData([
...bg,
Expand Down Expand Up @@ -83,24 +88,31 @@ export const locales = [
'zh'
]

function getDefaltLocale() {
const store = new Store({ name: 'settings' })

// Detect user language.
let language = store.get('locale') || (app || remote.app).getLocale()

// If the detected language is not available, strip out any regional component and check again.
if (!locales.includes(language)) {
language = language.toLowerCase().split(/[_-]+/)[0]
}
// If we still can't find the users language, default to english.
if (!locales.includes(language)) {
language = 'en'
}
return language
}

export const DEFAULT_LOCALE = getDefaltLocale()
// Defaine list of currencies that we will support.
export const currencies = [
'USD',
'EUR',
'JPY',
'GBP',
'CAD',
'KRW',
'AUD',
'BRL',
'CHF',
'CLP',
'CNY',
'DKK',
'HKD',
'INR',
'ISK',
'NZD',
'PLN',
'RUB',
'SEK',
'SGD',
'THB',
'TWB'
]

// Collate all translations.
export const translationMessages = {
Expand All @@ -123,3 +135,62 @@ export const translationMessages = {
tr: trTranslationMessages,
uk: ukTranslationMessages
}

/**
* Get the most appropriate language code.
* @return {string} Language code.
*/
export const getLocale = () => {
const store = new Store({ name: 'settings' })
const userLocale = store.get('locale')
if (userLocale) {
debug('Determined locale as %s from settings', userLocale)
return userLocale
}
const defaultLocale = (app || remote.app).getLocale() || 'en-US'
const language = defaultLocale.toLowerCase().split(/[_-]+/)[0]
let locale = 'en'
if (locales.includes(language)) {
locale = language
}
if (locales.includes(defaultLocale)) {
locale = userLocale
}
debug('Determined locale as %s', locale)
return locale
}

/**
* Get the most appropriate language code.
* @return {string} Language code.
*/
export const getLanguageName = lang => {
const language = lang.toLowerCase().split(/[_-]+/)[0]
const data = lookup.languages({ alpha2: language })
const name = get(data, '[0]name', language)
debug('Determined language as %s', name)
return name
}

/**
* Get the most appropriate currency code.
* @return {string} Currency code.
*/
export const getCurrency = () => {
const store = new Store({ name: 'settings' })
const userCurrency = store.get('fiatTicker')
if (userCurrency) {
debug('Determined currency as %s from settings', userCurrency)
return userCurrency
}
const defaultLocale = (app || remote.app).getLocale() || 'en-US'
const country = defaultLocale.split(/[_-]+/)[1]
const data = lookup.countries({ alpha2: country })
const detectedCurrency = get(data, '[0]currencies[0]', 'USD')
let currency = 'USD'
if (currencies.includes(detectedCurrency)) {
currency = detectedCurrency
}
debug('Determined currency as %s', currency)
return currency
}
7 changes: 3 additions & 4 deletions app/lib/zap/menuBuilder.js
@@ -1,15 +1,14 @@
// @flow
import { app, Menu, shell, BrowserWindow, ipcMain } from 'electron'
import ISO6391 from 'iso-639-1'
import { locales, DEFAULT_LOCALE } from '../utils/i18n'
import { locales, getLocale, getLanguageName } from '../utils/i18n'

export default class ZapMenuBuilder {
mainWindow: BrowserWindow
locale: string

constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow
this.locale = DEFAULT_LOCALE
this.locale = getLocale()
ipcMain.on('setLocale', (event, locale) => this.buildMenu(locale))
}

Expand Down Expand Up @@ -281,7 +280,7 @@ export default class ZapMenuBuilder {
label: 'Language',
submenu: locales.map(locale => {
return {
label: ISO6391.getName(locale.split('-')[0]),
label: getLanguageName(locale),
type: 'radio',
checked: this.locale === locale,
click: () => this.mainWindow.webContents.send('receiveLocale', locale)
Expand Down
28 changes: 3 additions & 25 deletions app/reducers/ticker.js
@@ -1,6 +1,7 @@
import { createSelector } from 'reselect'
import Store from 'electron-store'
import { requestTicker } from 'lib/utils/api'
import { currencies, getCurrency } from 'lib/utils/i18n'
import { infoSelectors } from './info'

// Settings store
Expand Down Expand Up @@ -141,31 +142,8 @@ const initialState = {
crypto: '',
btcTicker: null,
ltcTicker: null,
fiatTicker: store.get('fiatTicker', 'USD'),
fiatTickers: [
'USD',
'EUR',
'JPY',
'GBP',
'CAD',
'KRW',
'AUD',
'BRL',
'CHF',
'CLP',
'CNY',
'DKK',
'HKD',
'INR',
'ISK',
'NZD',
'PLN',
'RUB',
'SEK',
'SGD',
'THB',
'TWB'
],
fiatTicker: getCurrency(),
fiatTickers: currencies,
currencyFilters: [
{
key: 'btc',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -286,6 +286,7 @@
"axios": "^0.18.0",
"bitcoinjs-lib": "^4.0.1",
"copy-to-clipboard": "^3.0.8",
"country-data-lookup": "^0.0.3",
"debug": "^4.0.1",
"debug-logger": "^0.4.1",
"devtron": "^1.4.0",
Expand All @@ -295,7 +296,6 @@
"electron-store": "^2.0.0",
"font-awesome": "^4.7.0",
"history": "^4.7.2",
"iso-639-1": "^2.0.3",
"javascript-state-machine": "^3.1.0",
"jstimezonedetect": "^1.0.6",
"lodash.get": "^4.4.2",
Expand Down
9 changes: 7 additions & 2 deletions test/unit/__mocks__/electron.js
Expand Up @@ -5,9 +5,14 @@ module.exports = {
match: jest.fn(),
app: {
getPath: name => normalize(`/tmp/zap-test/${name}`),
getAppPath: () => normalize('/tmp/zap-test')
getAppPath: () => normalize('/tmp/zap-test'),
getLocale: jest.fn()
},
remote: {
app: {
getLocale: jest.fn()
}
},
remote: jest.fn(),
dialog: jest.fn(),
BrowserWindow: jest.fn(),
ipcMain: {
Expand Down
15 changes: 11 additions & 4 deletions yarn.lock
Expand Up @@ -3438,6 +3438,13 @@ cosmiconfig@^5.0.6:
js-yaml "^3.9.0"
parse-json "^4.0.0"

country-data-lookup@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/country-data-lookup/-/country-data-lookup-0.0.3.tgz#59babadbd70c74a870f78e518b517b210bd8e3dd"
dependencies:
currency-symbol-map "~4.0.3"
lodash "~4.17.4"

coveralls@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.2.tgz#f5a0bcd90ca4e64e088b710fa8dda640aea4884f"
Expand Down Expand Up @@ -3663,6 +3670,10 @@ cssstyle@^1.0.0:
dependencies:
cssom "0.3.x"

currency-symbol-map@~4.0.3:
version "4.0.4"
resolved "https://registry.yarnpkg.com/currency-symbol-map/-/currency-symbol-map-4.0.4.tgz#3cfba625974dd3f86822d327ecbd10248695e95e"

currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
Expand Down Expand Up @@ -6835,10 +6846,6 @@ isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"

iso-639-1@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/iso-639-1/-/iso-639-1-2.0.3.tgz#72dd3448ac5629c271628c5ac566369428d6ccd0"

isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
Expand Down

0 comments on commit 27e46cc

Please sign in to comment.