i18n #80
10
src/i18n.js
|
@ -1,10 +1,13 @@
|
||||||
![]() No, the commented code can be removed. No, the commented code can be removed.
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
import { NativeModules, Platform } from 'react-native';
|
import { NativeModules, Platform } from 'react-native';
|
||||||
|
import { SETTINGS } from 'lbry-redux';
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
import { doTransifexUpload } from 'lbryinc';
|
import { doTransifexUpload } from 'lbryinc';
|
||||||
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
import RNFS from 'react-native-fs';
|
import RNFS from 'react-native-fs';
|
||||||
|
|
||||||
const isProduction = !__DEV__; // eslint-disable-line no-undef
|
const isProduction = !__DEV__; // eslint-disable-line no-undef
|
||||||
let knownMessages = null;
|
let knownMessages = null;
|
||||||
|
|
||||||
|
window.language = 'en';
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
window.i18n_messages = window.i18n_messages || {};
|
window.i18n_messages = window.i18n_messages || {};
|
||||||
|
|
||||||
function saveMessage(message) {
|
function saveMessage(message) {
|
||||||
|
@ -55,11 +58,12 @@ function checkMessageAndSave(message, messagesFilePath) {
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function __(message, tokens) {
|
export function __(message, tokens) {
|
||||||
let language =
|
let language = window.language;
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
Platform.OS === 'android'
|
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
|
/* Platform.OS === 'android'
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
? NativeModules.I18nManager.localeIdentifier
|
? NativeModules.I18nManager.localeIdentifier
|
||||||
: NativeModules.SettingsManager.settings.AppleLocale;
|
: NativeModules.SettingsManager.settings.AppleLocale;
|
||||||
language = language ? language.substring(0, 2) : 'en';
|
window.language = language ? language.substring(0, 2) : 'en'; */
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|||||||
|
|
||||||
if (!isProduction) {
|
if (!isProduction) {
|
||||||
saveMessage(message);
|
saveMessage(message);
|
||||||
|
|
||||||
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
![]() do you want this here? do you want this here?
![]() No, the commented code can be removed. No, the commented code can be removed.
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SETTINGS } from 'lbry-redux';
|
import { SETTINGS, doToast } from 'lbry-redux';
|
||||||
import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import { selectCurrentRoute, selectDrawerStack } from 'redux/selectors/drawer';
|
import { selectCurrentRoute, selectDrawerStack } from 'redux/selectors/drawer';
|
||||||
|
@ -12,6 +12,7 @@ const select = state => ({
|
||||||
currentRoute: selectCurrentRoute(state),
|
currentRoute: selectCurrentRoute(state),
|
||||||
drawerStack: selectDrawerStack(state),
|
drawerStack: selectDrawerStack(state),
|
||||||
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
|
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
|
||||||
|
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
|
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
|
||||||
showUriBarSuggestions: makeSelectClientSetting(SETTINGS.SHOW_URI_BAR_SUGGESTIONS)(state),
|
showUriBarSuggestions: makeSelectClientSetting(SETTINGS.SHOW_URI_BAR_SUGGESTIONS)(state),
|
||||||
receiveSubscriptionNotifications: makeSelectClientSetting(SETTINGS.RECEIVE_SUBSCRIPTION_NOTIFICATIONS)(state),
|
receiveSubscriptionNotifications: makeSelectClientSetting(SETTINGS.RECEIVE_SUBSCRIPTION_NOTIFICATIONS)(state),
|
||||||
|
@ -21,6 +22,7 @@ const select = state => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
notify: data => dispatch(doToast(data)),
|
||||||
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SETTINGS)),
|
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SETTINGS)),
|
||||||
popDrawerStack: () => dispatch(doPopDrawerStack()),
|
popDrawerStack: () => dispatch(doPopDrawerStack()),
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
|
|
|
@ -1,12 +1,32 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SETTINGS } from 'lbry-redux';
|
import { SETTINGS } from 'lbry-redux';
|
||||||
import { Text, View, ScrollView, Switch, NativeModules } from 'react-native';
|
import { ActivityIndicator, Picker, Platform, Text, View, ScrollView, Switch, NativeModules } from 'react-native';
|
||||||
import { navigateBack } from 'utils/helper';
|
import { navigateBack } from 'utils/helper';
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
|
import Colors from 'styles/colors';
|
||||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||||
import PageHeader from 'component/pageHeader';
|
import PageHeader from 'component/pageHeader';
|
||||||
|
import RNFS from 'react-native-fs';
|
||||||
import settingsStyle from 'styles/settings';
|
import settingsStyle from 'styles/settings';
|
||||||
|
|
||||||
|
const languageOptions = [
|
||||||
|
{ code: 'default', name: __('Use device language') },
|
||||||
|
{ code: 'en', name: __('English') },
|
||||||
|
{ code: 'gu', name: __('Gujarati') },
|
||||||
|
{ code: 'hi', name: __('Hindi') },
|
||||||
|
{ code: 'id', name: __('Indonesian') },
|
||||||
|
{ code: 'ms', name: __('Malay') },
|
||||||
|
{ code: 'pl', name: __('Polish') },
|
||||||
|
{ code: 'pt', name: __('Portuguese') },
|
||||||
|
{ code: 'es', name: __('Spanish') },
|
||||||
|
];
|
||||||
|
|
||||||
class SettingsPage extends React.PureComponent {
|
class SettingsPage extends React.PureComponent {
|
||||||
|
state = {
|
||||||
|
downloadingLanguage: false,
|
||||||
|
};
|
||||||
|
|
||||||
static navigationOptions = {
|
static navigationOptions = {
|
||||||
title: 'Settings',
|
title: 'Settings',
|
||||||
};
|
};
|
||||||
|
@ -53,17 +73,68 @@ class SettingsPage extends React.PureComponent {
|
||||||
return value === null || value === undefined ? defaultValue : value;
|
return value === null || value === undefined ? defaultValue : value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleLanguageValueChange = value => {
|
||||||
|
const { notify, setClientSetting } = this.props;
|
||||||
|
|
||||||
|
let language;
|
||||||
|
if (value === 'default') {
|
||||||
|
language =
|
||||||
|
Platform.OS === 'android'
|
||||||
|
? NativeModules.I18nManager.localeIdentifier
|
||||||
|
: NativeModules.SettingsManager.settings.AppleLocale;
|
||||||
|
language = language ? language.substring(0, 2) : 'en';
|
||||||
|
} else {
|
||||||
|
language = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the local filesystem for the language first? Or download remote strings first?
|
||||||
|
|
||||||
|
// download and save the language file
|
||||||
|
this.setState({ downloadingLanguage: true }, () => {
|
||||||
|
fetch('https://lbry.com/i18n/get/lbry-mobile/app-strings/' + language + '.json')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(j => {
|
||||||
|
window.i18n_messages[language] = j;
|
||||||
|
|
||||||
|
console.log(window.i18n_messages);
|
||||||
|
|
||||||
|
// write the language file to the filesystem
|
||||||
|
const langFilePath = RNFS.ExternalDirectoryPath + '/' + language + '.json';
|
||||||
|
RNFS.writeFile(langFilePath, JSON.stringify(j), 'utf8');
|
||||||
|
|
||||||
|
// update state and client setting
|
||||||
|
window.language = language;
|
||||||
|
setClientSetting(SETTINGS.LANGUAGE, value);
|
||||||
|
|
||||||
|
this.setState({ downloadingLanguage: false });
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
notify({ message: __('Failed to load %language% translations.', { language: language }), isError: true });
|
||||||
|
this.setState({ downloadingLanguage: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleBackPressed = () => {
|
||||||
|
const { navigation, notify, drawerStack, popDrawerStack } = this.props;
|
||||||
|
|
||||||
|
if (this.state.downloadingLanguage) {
|
||||||
|
notify({ message: 'Please wait for the language file to finish downloading' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateBack(navigation, drawerStack, popDrawerStack);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
![]() I believe this needs to happen on startup and/or at some other interval to ensure a user receives new translations after changing their language. I believe this needs to happen on startup and/or at some other interval to ensure a user receives new translations after changing their language.
![]() I can make this happen on startup. We probably don't need to do an interval since it's unlikely the translations are going to be changing that often. I can make this happen on startup. We probably don't need to do an interval since it's unlikely the translations are going to be changing that often.
|
|||||||
backgroundPlayEnabled,
|
backgroundPlayEnabled,
|
||||||
drawerStack,
|
|
||||||
keepDaemonRunning,
|
keepDaemonRunning,
|
||||||
navigation,
|
|
||||||
popDrawerStack,
|
|
||||||
receiveSubscriptionNotifications,
|
receiveSubscriptionNotifications,
|
||||||
receiveRewardNotifications,
|
receiveRewardNotifications,
|
||||||
receiveInterestsNotifications,
|
receiveInterestsNotifications,
|
||||||
receiveCreatorNotifications,
|
receiveCreatorNotifications,
|
||||||
|
language,
|
||||||
showNsfw,
|
showNsfw,
|
||||||
showUriBarSuggestions,
|
showUriBarSuggestions,
|
||||||
setClientSetting,
|
setClientSetting,
|
||||||
|
@ -78,10 +149,7 @@ class SettingsPage extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={settingsStyle.container}>
|
<View style={settingsStyle.container}>
|
||||||
<PageHeader
|
<PageHeader title={__('Settings')} onBackPressed={this.handleBackPressed} />
|
||||||
title={__('Settings')}
|
|
||||||
onBackPressed={() => navigateBack(navigation, drawerStack, popDrawerStack)}
|
|
||||||
/>
|
|
||||||
<ScrollView style={settingsStyle.scrollContainer}>
|
<ScrollView style={settingsStyle.scrollContainer}>
|
||||||
<Text style={settingsStyle.sectionTitle}>{__('Content')}</Text>
|
<Text style={settingsStyle.sectionTitle}>{__('Content')}</Text>
|
||||||
<View style={settingsStyle.row}>
|
<View style={settingsStyle.row}>
|
||||||
|
@ -99,6 +167,27 @@ class SettingsPage extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<Text style={settingsStyle.sectionTitle}>{__('Language')}</Text>
|
||||||
|
<View style={settingsStyle.row}>
|
||||||
|
<View style={settingsStyle.pickerText}>
|
||||||
|
<Text style={settingsStyle.label}>{__('Choose language')}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={settingsStyle.pickerContainer}>
|
||||||
|
{this.state.downloadingLanguage && <ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />}
|
||||||
|
<Picker
|
||||||
|
enabled={!this.state.downloadingLanguage}
|
||||||
|
selectedValue={language || 'default'}
|
||||||
|
style={settingsStyle.languagePicker}
|
||||||
|
itemStyle={settingsStyle.languagePickerItem}
|
||||||
|
onValueChange={this.handleLanguageValueChange}
|
||||||
|
>
|
||||||
|
{languageOptions.map(option => (
|
||||||
|
<Picker.Item label={option.name} value={option.code} key={option.code} />
|
||||||
|
))}
|
||||||
|
</Picker>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
<View style={settingsStyle.row}>
|
<View style={settingsStyle.row}>
|
||||||
<View style={settingsStyle.switchText}>
|
<View style={settingsStyle.switchText}>
|
||||||
<Text style={settingsStyle.label}>{__('Show mature content')}</Text>
|
<Text style={settingsStyle.label}>{__('Show mature content')}</Text>
|
||||||
|
|
|
@ -28,6 +28,12 @@ const settingsStyle = StyleSheet.create({
|
||||||
width: '25%',
|
width: '25%',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
|
pickerText: {
|
||||||
|
width: '50%',
|
||||||
|
},
|
||||||
|
pickerContainer: {
|
||||||
|
width: '50%',
|
||||||
|
},
|
||||||
label: {
|
label: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontFamily: 'Inter-UI-Regular',
|
fontFamily: 'Inter-UI-Regular',
|
||||||
|
|
do you want this here?