Localization (and language chooser)...

This commit is contained in:
Eric van der Vlist 2022-10-08 17:33:32 +02:00
parent 653574025e
commit 5822198f92
6 changed files with 93 additions and 10 deletions

View File

@ -29,7 +29,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { mapActions } from '../../store/map'; import { mapActions } from '../../store/map';
import { SettingsState } from '../../store/settings'; import { SettingsState } from '../../store/settings';
import i18n from '../../i18n/index'; import i18n, { setI18nLanguage } from '../../i18n/index';
declare global { declare global {
@ -37,6 +37,13 @@ declare global {
} }
const GpxRecord: React.FC<{}> = () => { const GpxRecord: React.FC<{}> = () => {
const language = useSelector(
(state: { settings: SettingsState }) => state.settings.language
);
setI18nLanguage(language);
const db = useDB(); const db = useDB();
const [isRecording, setIsRecording] = useState(false); const [isRecording, setIsRecording] = useState(false);

View File

@ -1,4 +1,4 @@
import React, { Fragment, useEffect, useRef } from 'react'; import React, { Fragment, useEffect, useRef, useState } from 'react';
import { useDB } from 'react-pouchdb'; import { useDB } from 'react-pouchdb';
@ -13,6 +13,8 @@ import {
IonList, IonList,
IonListHeader, IonListHeader,
IonModal, IonModal,
IonSelect,
IonSelectOption,
IonTitle, IonTitle,
IonToolbar, IonToolbar,
} from '@ionic/react'; } from '@ionic/react';
@ -22,7 +24,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { settingsActions, SettingsState } from '../../store/settings'; import { settingsActions, SettingsState } from '../../store/settings';
import uri from '../../lib/ids'; import uri from '../../lib/ids';
import _ from 'lodash'; import _ from 'lodash';
import i18n from '../../i18n/index'; import i18n, { setI18nLanguage } from '../../i18n/index';
const Settings: React.FC<{}> = () => { const Settings: React.FC<{}> = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -66,18 +68,18 @@ const Settings: React.FC<{}> = () => {
}; };
settingsFromRedux(); settingsFromRedux();
} }
setLocalLanguage(settingsState.language);
}, [db, settingsState]); }, [db, settingsState]);
const [localLanguage, setLocalLanguage] = useState(settingsState.language);
const languageSelect = useRef<HTMLIonSelectElement>(null);
const pseudo = useRef<HTMLIonInputElement>(null); const pseudo = useRef<HTMLIonInputElement>(null);
const minTimeInterval = useRef<HTMLIonInputElement>(null); const minTimeInterval = useRef<HTMLIonInputElement>(null);
const minDistance = useRef<HTMLIonInputElement>(null); const minDistance = useRef<HTMLIonInputElement>(null);
const modal = useRef<HTMLIonModalElement>(null); const modal = useRef<HTMLIonModalElement>(null);
const dismiss = () => {
modal.current?.dismiss();
};
const save = () => { const save = () => {
dispatch( dispatch(
settingsActions.saveSettings({ settingsActions.saveSettings({
@ -88,11 +90,19 @@ const Settings: React.FC<{}> = () => {
minTimeInterval: minTimeInterval.current?.value, minTimeInterval: minTimeInterval.current?.value,
minDistance: minDistance.current?.value, minDistance: minDistance.current?.value,
}, },
language: languageSelect.current?.value,
}) })
); );
dismiss(); dismiss();
}; };
setI18nLanguage(localLanguage);
const dismiss = () => {
modal.current?.dismiss();
setLocalLanguage(settingsState.language);
};
return ( return (
<Fragment> <Fragment>
<IonButton id='open-SettingsDialog'> <IonButton id='open-SettingsDialog'>
@ -112,6 +122,27 @@ const Settings: React.FC<{}> = () => {
</IonButtons> </IonButtons>
</IonToolbar> </IonToolbar>
<IonContent> <IonContent>
<IonList lines='full' class='ion-no-margin'>
<IonListHeader lines='full'>
<IonLabel>{i18n.language}</IonLabel>
</IonListHeader>
<IonItem>
<IonLabel>{i18n.colonize(i18n.language)} </IonLabel>
<IonSelect
placeholder={i18n.languageSelect.placeHolder}
ref={languageSelect}
value={localLanguage}
interface='popover'
onIonChange={(ev) => setLocalLanguage(ev.detail.value)}
>
{i18n.languageSelect.choices.map((choice) => (
<IonSelectOption key={choice.value} value={choice.value}>
{choice.label}
</IonSelectOption>
))}
</IonSelect>
</IonItem>
</IonList>
<IonList lines='full' class='ion-no-margin'> <IonList lines='full' class='ion-no-margin'>
<IonListHeader lines='full'> <IonListHeader lines='full'>
<IonLabel>{i18n.user}</IonLabel> <IonLabel>{i18n.user}</IonLabel>

View File

@ -16,13 +16,20 @@ import { useDispatch, useSelector } from 'react-redux';
import { enterAnimation, leaveAnimation } from '../../lib/animation'; import { enterAnimation, leaveAnimation } from '../../lib/animation';
import { mapActions, MapState } from '../../store/map'; import { mapActions, MapState } from '../../store/map';
import { tileProviders } from './tile'; import { tileProviders } from './tile';
import i18n from '../../i18n/index'; import i18n, { setI18nLanguage } from '../../i18n/index';
import { SettingsState } from '../../store/settings';
const TileServerChooserDialog: React.FC<{}> = () => { const TileServerChooserDialog: React.FC<{}> = () => {
const tileProvider = useSelector( const tileProvider = useSelector(
(state: { map: MapState }) => state.map.scope.tileProvider (state: { map: MapState }) => state.map.scope.tileProvider
) as keyof typeof tileProviders; ) as keyof typeof tileProviders;
const language = useSelector(
(state: { settings: SettingsState }) => state.settings.language
);
setI18nLanguage(language);
const dispatch = useDispatch(); const dispatch = useDispatch();
const modal = useRef<HTMLIonModalElement>(null); const modal = useRef<HTMLIonModalElement>(null);

View File

@ -21,7 +21,9 @@ import { enterAnimation, leaveAnimation } from '../../lib/animation';
import phoneRoute from '../../theme/icons/font-gis/svg/routing/uEB08-phone-route-nons.svg'; import phoneRoute from '../../theme/icons/font-gis/svg/routing/uEB08-phone-route-nons.svg';
import GpxImport from './gpx-import'; import GpxImport from './gpx-import';
import GpxExport from './GpxExport'; import GpxExport from './GpxExport';
import i18n from '../../i18n/index'; import i18n, { setI18nLanguage } from '../../i18n/index';
import { useSelector } from 'react-redux';
import { SettingsState } from '../../store/settings';
const TrackBrowser: React.FC<{}> = () => { const TrackBrowser: React.FC<{}> = () => {
const gpxes = useFind({ const gpxes = useFind({
@ -38,6 +40,13 @@ const TrackBrowser: React.FC<{}> = () => {
modal.current?.dismiss(); modal.current?.dismiss();
}; };
const language = useSelector(
(state: { settings: SettingsState }) => state.settings.language
);
setI18nLanguage(language);
return ( return (
<Fragment> <Fragment>
<IonButton id='track-browser-button'> <IonButton id='track-browser-button'>

View File

@ -1,4 +1,3 @@
import { Fragment } from 'react';
import LocalizedStrings from 'react-localization'; import LocalizedStrings from 'react-localization';
const strings = new LocalizedStrings({ const strings = new LocalizedStrings({
@ -9,6 +8,7 @@ const strings = new LocalizedStrings({
cancel: 'Cancel', cancel: 'Cancel',
close: 'Close', close: 'Close',
settings: 'Settings', settings: 'Settings',
language: 'Language',
user: 'User', user: 'User',
pseudo: 'Pseudo', pseudo: 'Pseudo',
geolocation: 'Geolocation', geolocation: 'Geolocation',
@ -38,6 +38,15 @@ const strings = new LocalizedStrings({
chooseYourMap: 'Choose your map', chooseYourMap: 'Choose your map',
languageSelect: {
placeHolder: 'Select your language',
choices: [
{ value: 'auto', label: 'Automatic' },
{ value: 'en', label: 'English' },
{ value: 'fr', label: 'Français' },
],
},
tracks: 'Tracks', tracks: 'Tracks',
}, },
fr: { fr: {
@ -47,6 +56,7 @@ const strings = new LocalizedStrings({
cancel: 'Annuler', cancel: 'Annuler',
close: 'Fermer', close: 'Fermer',
settings: 'Paramètres', settings: 'Paramètres',
language: 'Langue',
user: 'Utilisateur', user: 'Utilisateur',
pseudo: 'Pseudo', pseudo: 'Pseudo',
geolocation: 'Géolocalisation', geolocation: 'Géolocalisation',
@ -86,8 +96,25 @@ const strings = new LocalizedStrings({
chooseYourMap: 'Choisissez votre carte', chooseYourMap: 'Choisissez votre carte',
languageSelect: {
placeHolder: 'Selectionner votre langue',
choices: [
{ value: 'auto', label: 'Automatique' },
{ value: 'fr', label: 'Français' },
{ value: 'en', label: 'English' },
],
},
tracks: 'Traces', tracks: 'Traces',
}, },
}); });
export default strings; export default strings;
export const setI18nLanguage = (language: string) => {
if (language === undefined || language === 'auto') {
strings.setLanguage(strings.getInterfaceLanguage());
} else {
strings.setLanguage(language);
}
};

View File

@ -9,6 +9,7 @@ export interface SettingsState {
minTimeInterval: number; minTimeInterval: number;
minDistance: number; minDistance: number;
}; };
language: 'auto'
} }
export const initialSettingsState: SettingsState = { export const initialSettingsState: SettingsState = {
@ -20,6 +21,7 @@ export const initialSettingsState: SettingsState = {
minTimeInterval: 15000, minTimeInterval: 15000,
minDistance: 10, minDistance: 10,
}, },
language: 'auto',
}; };
const settingsSlice = createSlice({ const settingsSlice = createSlice({