Merge pull request #4804 from BlueWallet/refactor-loc

REF: refactor localization
This commit is contained in:
GLaDOS 2022-06-19 19:52:33 +01:00 committed by GitHub
commit 2cf94fd748
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 70 deletions

3
App.js
View File

@ -12,6 +12,7 @@ import {
useColorScheme,
View,
StatusBar,
LogBox,
} from 'react-native';
import { NavigationContainer, CommonActions } from '@react-navigation/native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
@ -42,6 +43,8 @@ const currency = require('./blue_modules/currency');
const eventEmitter = Platform.OS === 'ios' ? new NativeEventEmitter(NativeModules.EventEmitter) : undefined;
const { EventEmitter } = NativeModules;
LogBox.ignoreLogs(['Require cycle:']);
const ClipboardContentType = Object.freeze({
BITCOIN: 'BITCOIN',
LIGHTNING: 'LIGHTNING',

View File

@ -5,7 +5,7 @@ import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { useAsyncStorage } from '@react-native-async-storage/async-storage';
import { FiatUnit } from '../models/fiatUnit';
import Notifications from '../blue_modules/notifications';
import loc from '../loc';
import loc, { STORAGE_KEY as LOC_STORAGE_KEY } from '../loc';
import { LegacyWallet } from '../class';
import { isTorDaemonDisabled, setIsTorDaemonDisabled } from './environment';
import alert from '../components/Alert';
@ -26,7 +26,7 @@ export const BlueStorageProvider = ({ children }) => {
const [preferredFiatCurrency, _setPreferredFiatCurrency] = useState(FiatUnit.USD);
const [language, _setLanguage] = useState();
const getPreferredCurrencyAsyncStorage = useAsyncStorage(currency.PREFERRED_CURRENCY).getItem;
const getLanguageAsyncStorage = useAsyncStorage(loc.LANG).getItem;
const getLanguageAsyncStorage = useAsyncStorage(LOC_STORAGE_KEY).getItem;
const [isHandOffUseEnabled, setIsHandOffUseEnabled] = useState(false);
const [isElectrumDisabled, setIsElectrumDisabled] = useState(true);
const [isTorDisabled, setIsTorDisabled] = useState(false);

View File

@ -11,15 +11,13 @@ import { AvailableLanguages } from './languages';
import { I18nManager } from 'react-native';
const currency = require('../blue_modules/currency');
const LANG = 'lang';
export const STORAGE_KEY = 'lang';
dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);
// first-time loading sequence
const setDateTimeLocale = async () => {
let lang = await AsyncStorage.getItem('lang');
let lang = (await AsyncStorage.getItem(STORAGE_KEY)) ?? '';
let localeForDayJSAvailable = true;
switch (lang) {
case 'ar':
@ -148,51 +146,35 @@ const setDateTimeLocale = async () => {
}
if (localeForDayJSAvailable) {
dayjs.locale(lang.split('_')[0]);
const language = AvailableLanguages.find(language => language.value === lang.replace('_', '-'));
/* I18n Manager breaks testing. Mocking built-in RN modules is not so straightforward.
Only run this conditional if its outside a testing environment.
*/
if (process.env.JEST_WORKER_ID === undefined) {
if (language?.isRTL) {
I18nManager.allowRTL(true);
I18nManager.forceRTL(true);
} else {
I18nManager.allowRTL(false);
I18nManager.forceRTL(false);
}
}
} else {
dayjs.locale('en');
if (process.env.JEST_WORKER_ID === undefined) {
I18nManager.allowRTL(false);
I18nManager.forceRTL(false);
}
}
};
const setLanguageLocale = async () => {
const init = async () => {
// finding out whether lang preference was saved
const lang = await AsyncStorage.getItem(LANG);
const lang = await AsyncStorage.getItem(STORAGE_KEY);
if (lang) {
await strings.saveLanguage(lang);
await strings.setLanguage(lang);
await saveLanguage(lang);
await loc.setLanguage(lang);
if (process.env.JEST_WORKER_ID === undefined) {
I18nManager.allowRTL(lang.isRTL ?? false);
I18nManager.forceRTL(lang.isRTL ?? false);
const foundLang = AvailableLanguages.find(language => language.value === lang);
I18nManager.allowRTL(foundLang?.isRTL ?? false);
I18nManager.forceRTL(foundLang?.isRTL ?? false);
}
await setDateTimeLocale();
} else {
const locales = RNLocalize.getLocales();
if (Object.values(AvailableLanguages).some(language => language.value === locales[0].languageCode)) {
await strings.saveLanguage(locales[0].languageCode);
await strings.setLanguage(locales[0].languageCode);
await saveLanguage(locales[0].languageCode);
await loc.setLanguage(locales[0].languageCode);
if (process.env.JEST_WORKER_ID === undefined) {
I18nManager.allowRTL(locales[0].isRTL ?? false);
I18nManager.forceRTL(locales[0].isRTL ?? false);
}
} else {
await strings.saveLanguage('en');
await strings.setLanguage('en');
await saveLanguage('en');
await loc.setLanguage('en');
if (process.env.JEST_WORKER_ID === undefined) {
I18nManager.allowRTL(false);
I18nManager.forceRTL(false);
@ -201,14 +183,9 @@ const setLanguageLocale = async () => {
await setDateTimeLocale();
}
};
setLanguageLocale();
init();
/**
* TODO: remove this comment once this file gets properly converted to typescript.
*
* @type {any}
*/
const strings = new Localization({
const loc = new Localization({
en: require('./en.json'),
ar: require('./ar.json'),
bg_bg: require('./bg_bg.json'),
@ -253,18 +230,24 @@ const strings = new Localization({
zh_tw: require('./zh_tw.json'),
});
strings.saveLanguage = async lang => {
await AsyncStorage.setItem(LANG, lang);
strings.setLanguage(lang);
export const saveLanguage = async (lang: string) => {
await AsyncStorage.setItem(STORAGE_KEY, lang);
loc.setLanguage(lang);
// even tho it makes no effect changing it in this run, it will on the next run, so we are doign it here:
if (process.env.JEST_WORKER_ID === undefined) {
const foundLang = AvailableLanguages.find(language => language.value === lang);
I18nManager.allowRTL(foundLang?.isRTL ?? false);
I18nManager.forceRTL(foundLang?.isRTL ?? false);
}
await setDateTimeLocale();
};
export const transactionTimeToReadable = time => {
export const transactionTimeToReadable = (time: number) => {
if (time === -1) {
return 'unknown';
}
if (time === 0) {
return strings._.never;
return loc._.never;
}
let ret;
try {
@ -276,34 +259,34 @@ export const transactionTimeToReadable = time => {
return ret;
};
export const removeTrailingZeros = value => {
value = value.toString();
export const removeTrailingZeros = (value: number | string) => {
let ret = value.toString();
if (value.indexOf('.') === -1) {
return value;
if (ret.indexOf('.') === -1) {
return ret;
}
while ((value.slice(-1) === '0' || value.slice(-1) === '.') && value.indexOf('.') !== -1) {
value = value.substr(0, value.length - 1);
while ((ret.slice(-1) === '0' || ret.slice(-1) === '.') && ret.indexOf('.') !== -1) {
ret = ret.substr(0, ret.length - 1);
}
return value;
return ret;
};
/**
*
* @param balance {number} Satoshis
* @param toUnit {String} Value from models/bitcoinUnits.js
* @param toUnit {string} Value from models/bitcoinUnits.js
* @param withFormatting {boolean} Works only with `BitcoinUnit.SATS`, makes spaces wetween groups of 000
* @returns {string}
*/
export function formatBalance(balance, toUnit, withFormatting = false) {
export function formatBalance(balance: number, toUnit: string, withFormatting = false) {
if (toUnit === undefined) {
return balance + ' ' + strings.units[BitcoinUnit.BTC];
return balance + ' ' + loc.units[BitcoinUnit.BTC];
}
if (toUnit === BitcoinUnit.BTC) {
const value = new BigNumber(balance).dividedBy(100000000).toFixed(8);
return removeTrailingZeros(value) + ' ' + strings.units[BitcoinUnit.BTC];
return removeTrailingZeros(+value) + ' ' + loc.units[BitcoinUnit.BTC];
} else if (toUnit === BitcoinUnit.SATS) {
return (withFormatting ? new Intl.NumberFormat().format(balance).toString() : String(balance)) + ' ' + strings.units[BitcoinUnit.SATS];
return (withFormatting ? new Intl.NumberFormat().format(balance).toString() : String(balance)) + ' ' + loc.units[BitcoinUnit.SATS];
} else if (toUnit === BitcoinUnit.LOCAL_CURRENCY) {
return currency.satoshiToLocalCurrency(balance);
}
@ -311,12 +294,12 @@ export function formatBalance(balance, toUnit, withFormatting = false) {
/**
*
* @param balance {Integer} Satoshis
* @param toUnit {String} Value from models/bitcoinUnits.js, for example `BitcoinUnit.SATS`
* @param balance {number} Satoshis
* @param toUnit {string} Value from models/bitcoinUnits.js, for example `BitcoinUnit.SATS`
* @param withFormatting {boolean} Works only with `BitcoinUnit.SATS`, makes spaces wetween groups of 000
* @returns {string}
*/
export function formatBalanceWithoutSuffix(balance = 0, toUnit, withFormatting = false) {
export function formatBalanceWithoutSuffix(balance = 0, toUnit: string, withFormatting = false) {
if (toUnit === undefined) {
return balance;
}
@ -336,17 +319,18 @@ export function formatBalanceWithoutSuffix(balance = 0, toUnit, withFormatting =
/**
* Should be used when we need a simple string to be put in text input, for example
*
* @param balance {integer} Satoshis
* @param toUnit {String} Value from models/bitcoinUnits.js, for example `BitcoinUnit.SATS`
* @param balance {number} Satoshis
* @param toUnit {string} Value from models/bitcoinUnits.js, for example `BitcoinUnit.SATS`
* @param withFormatting {boolean} Works only with `BitcoinUnit.SATS`, makes spaces wetween groups of 000
* @returns {string}
*/
export function formatBalancePlain(balance = 0, toUnit, withFormatting = false) {
export function formatBalancePlain(balance = 0, toUnit: string, withFormatting = false) {
const newInputValue = formatBalanceWithoutSuffix(balance, toUnit, withFormatting);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return _leaveNumbersAndDots(newInputValue);
}
export function _leaveNumbersAndDots(newInputValue) {
export function _leaveNumbersAndDots(newInputValue: string) {
newInputValue = newInputValue.replace(/[^\d.,-]/g, ''); // filtering, leaving only numbers, dots & commas
if (newInputValue.endsWith('.00') || newInputValue.endsWith(',00')) newInputValue = newInputValue.substring(0, newInputValue.length - 3);
@ -359,10 +343,11 @@ export function _leaveNumbersAndDots(newInputValue) {
return newInputValue;
}
// https://github.com/BlueWallet/BlueWallet/issues/3466
export function formatStringAddTwoWhiteSpaces(text) {
/**
* @see https://github.com/BlueWallet/BlueWallet/issues/3466
*/
export function formatStringAddTwoWhiteSpaces(text: string): string {
return `${text} `;
}
strings.LANG = LANG;
export default strings;
export default loc;

View File

@ -4,7 +4,7 @@ import { useNavigation, useTheme } from '@react-navigation/native';
import navigationStyle from '../../components/navigationStyle';
import { BlueListItem } from '../../BlueComponents';
import loc from '../../loc';
import loc, { saveLanguage } from '../../loc';
import { AvailableLanguages } from '../../loc/languages';
import { BlueStorageContext } from '../../blue_modules/storage-context';
import alert from '../../components/Alert';
@ -36,7 +36,7 @@ const Language = () => {
<BlueListItem
onPress={() => {
const currentLanguage = AvailableLanguages.find(language => language.value === selectedLanguage);
loc.saveLanguage(item.item.value).then(() => {
saveLanguage(item.item.value).then(() => {
setSelectedLanguage(item.item.value);
setLanguage();
if (currentLanguage.isRTL || item.item.isRTL) {