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?