Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement language switching #289

Merged
merged 25 commits into from
Aug 7, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f4d373a
Update associated snapshots
Jul 28, 2021
0f22db8
Add translation for each languages for "Languages" settings entry
Jul 28, 2021
555079c
Introduce languages settings entry
Jul 28, 2021
35c4125
Add translations for language screen title
Jul 28, 2021
29e5a5e
Configure navigations to introduce languages settings page
Jul 28, 2021
eeaf01b
Add language screen
Jul 28, 2021
9f87c1b
Extract supported languages list for future convenience
Jul 28, 2021
469bc7d
Add language to redux user preferences store
Jul 28, 2021
8e31bad
Update test script
Jul 28, 2021
71b2a82
Minor design changes
Jul 28, 2021
f520c45
Merge branch 'main' into 'ImplementLanguageSwitching' (TopTab UI update)
Jul 29, 2021
f4577f8
Update snapshot
Jul 29, 2021
f6c9c9c
Write unit test for languages screen
Jul 29, 2021
0c7b2e2
Implement language switching through useContext
Jul 29, 2021
0b58b11
Rename SupportedLanguages.tsx to languages.tsx and move to utils/tran…
Aug 4, 2021
c3f558b
Add Danish and Chinese (Simplified) to languages list
Aug 4, 2021
06d46dc
Rename "sv" from Slovenian to Swedish
Aug 4, 2021
c085659
Index supportedLanguages and currentLanguage for easier import
Aug 4, 2021
3b9b3a8
Improve code for selecting current language
Aug 4, 2021
a6c3074
Modify language screen entry so that it won't appear in production mode
Aug 4, 2021
00e42bd
Minor code improvement
Aug 4, 2021
9f82e5c
Update test scripts
Aug 4, 2021
33fb7d7
Update snapshots
Aug 4, 2021
49cc05f
Change "sv" (settings screen) from Slovenian to Swedish
Aug 4, 2021
318fcb6
Merge branch 'main' into ImplementLanguageSwitching
PierreBresson Aug 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
exports[`userPreferences actions should export expected actions 1`] = `
Object {
"acceptTermsOfUse": [Function],
"changeLanguage": [Function],
"toggleNotifications": [Function],
"updateLocation": [Function],
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ElectricityType } from "carbon-footprint";
import { locale } from "expo-localization";

import SupportedLanguages from "../../../screens/Languages/SupportedLanguages";
import userPreferences from "../";

describe("userPreferences reducer should", () => {
Expand All @@ -10,6 +12,9 @@ describe("userPreferences reducer should", () => {
acceptedTermsOfUseVersion: 0,
activatedNotifications: false,
location: ElectricityType.world,
language: Object.keys(SupportedLanguages).includes(locale.substr(0, 2))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my other comment on SupportedLanguages.ts
Then you can do something like :

import { currentLanguage } from "utils";
...
language: currentLanguage

? locale.substr(0, 2)
: "en",
});
});

Expand All @@ -25,6 +30,9 @@ describe("userPreferences reducer should", () => {
acceptedTermsOfUseVersion: 2,
activatedNotifications: false,
location: ElectricityType.world,
language: Object.keys(SupportedLanguages).includes(locale.substr(0, 2))
? locale.substr(0, 2)
: "en",
});
});

Expand All @@ -38,6 +46,9 @@ describe("userPreferences reducer should", () => {
acceptedTermsOfUseVersion: 0,
activatedNotifications: false,
location: ElectricityType.belgium,
language: Object.keys(SupportedLanguages).includes(locale.substr(0, 2))
? locale.substr(0, 2)
: "en",
});
});

Expand All @@ -51,6 +62,23 @@ describe("userPreferences reducer should", () => {
acceptedTermsOfUseVersion: 0,
activatedNotifications: true,
location: ElectricityType.world,
language: Object.keys(SupportedLanguages).includes(locale.substr(0, 2))
? locale.substr(0, 2)
: "en",
});
});

it("handle language change", () => {
const expectedAction = {
type: userPreferences.actions.changeLanguage.toString(),
payload: "fr",
};

expect(userPreferences.reducer(undefined, expectedAction)).toEqual({
acceptedTermsOfUseVersion: 0,
activatedNotifications: false,
location: ElectricityType.world,
language: "fr",
});
});
});
10 changes: 10 additions & 0 deletions app/ducks/userPreferences/userPreferences.selectors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { pathOr } from "ramda";
import { ElectricityType } from "carbon-footprint";
import { locale } from "expo-localization";

import SupportedLanguages from "../../screens/Languages/SupportedLanguages";
import { namespace } from "./userPreferences.slice";

const getAcceptedTermsOfUseVersion = (state) =>
Expand All @@ -11,8 +13,16 @@ const getLocation = (state) => pathOr(ElectricityType.world, [namespace, "locati
const getActivateNotifications = (state) =>
pathOr(false, [namespace, "activatedNotifications"], state);

const getLanguage = (state) =>
pathOr(
Object.keys(SupportedLanguages).includes(locale.substr(0, 2)) ? locale.substr(0, 2) : "en",
[namespace, "language"],
state
);

export default {
getAcceptedTermsOfUseVersion,
getActivateNotifications,
getLocation,
getLanguage,
};
17 changes: 16 additions & 1 deletion app/ducks/userPreferences/userPreferences.slice.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { ElectricityType } from "carbon-footprint";
import { locale } from "expo-localization";

import SupportedLanguages from "../../screens/Languages/SupportedLanguages";

const initialState = {
acceptedTermsOfUseVersion: 0,
activatedNotifications: false,
location: ElectricityType.world,
language: Object.keys(SupportedLanguages).includes(locale.substr(0, 2))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Same here) see my other comment on SupportedLanguages.ts
Then you can do something like :

import { currentLanguage } from "utils";
...
language: currentLanguage

? locale.substr(0, 2)
: "en",
};

const userPreferences = createSlice({
Expand All @@ -20,15 +26,24 @@ const userPreferences = createSlice({
updateLocation(state, action: PayloadAction<ElectricityType>) {
state.location = action.payload;
},
changeLanguage(state, action: PayloadAction<string>) {
state.language = action.payload;
},
},
});

const { acceptTermsOfUse, updateLocation, toggleNotifications } = userPreferences.actions;
const {
acceptTermsOfUse,
updateLocation,
toggleNotifications,
changeLanguage,
} = userPreferences.actions;

export const actions = {
acceptTermsOfUse,
updateLocation,
toggleNotifications,
changeLanguage,
};

export const namespace = userPreferences.name;
Expand Down
6 changes: 6 additions & 0 deletions app/navigation/Navigator/BottomTab/SettingsNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import MyLocationScreen from "../../../screens/MyLocation";
import NotificationsScreen from "../../../screens/Notifications";
import MyData from "../../../screens/MyData";
import StorybookScreen from "../../../../storybook";
import LanguagesScreen from "../../../screens/Languages/LanguagesScreen";

const Stack = createStackNavigator();

Expand Down Expand Up @@ -36,6 +37,11 @@ const SettingsNavigator = (): React.ReactElement => (
/>
<Stack.Screen name="MyData" options={MyData.navigationOptions} component={MyData} />
<Stack.Screen name="Storybook" component={StorybookScreen} />
<Stack.Screen
name="Languages"
options={LanguagesScreen.navigationOptions}
component={LanguagesScreen}
/>
</Stack.Navigator>
);

Expand Down
5 changes: 5 additions & 0 deletions app/navigation/navigate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ const openMonthlyEmissions = (navigation) => (props = {}) => {
navigation.push("MonthlyEmissions", props);
};

const openLanguages = (navigation) => (props = {}) => {
navigation.push("Languages", props);
};

const navigate = (navigation) => ({
goBack: navigation.goBack,
openMonthlyEmissions: navigateOneTime(openMonthlyEmissions(navigation)),
Expand All @@ -89,6 +93,7 @@ const navigate = (navigation) => ({
openNotifications: navigateOneTime(openNotifications(navigation)),
openSupportUs: navigateOneTime(openSupportUs(navigation)),
openStorybook: navigateOneTime(openStorybook(navigation)),
openLanguages: navigateOneTime(openLanguages(navigation)),
});

export default navigate;
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ exports[`EmissionsScreen renders correctly 1`] = `
]
}
>
Friday
Thursday
</Text.Primary>
<Text.Primary
darkGray={true}
Expand All @@ -121,7 +121,7 @@ exports[`EmissionsScreen renders correctly 1`] = `
}
}
>
9th July 2021
29th July 2021
</Text.Primary>
</View>
<Button.Primary
Expand Down
17 changes: 17 additions & 0 deletions app/screens/Languages/LanguagesScreen.navigationOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react";

import { Text } from "components";
import { t } from "utils";
import { Colors, ComponentsStyle } from "style";

const navigationOptions = () => ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you have merged main into your branch, here is how to fix eslint issues :

import { StackNavigationOptions } from "@react-navigation/stack";
...
const navigationOptions = (): StackNavigationOptions => ({

...ComponentsStyle.transitionBetweenScreenPresets,
headerStyle: {
...ComponentsStyle.header,
},
headerBackTitleVisible: false,
headerTintColor: Colors.grey100,
headerTitle: () => <Text.H1>{t("LANGUAGES_SCREEN_TITLE")}</Text.H1>,
});

export default navigationOptions;
11 changes: 11 additions & 0 deletions app/screens/Languages/LanguagesScreen.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { StyleSheet } from "react-native";

import { Layout } from "style";

const styles = StyleSheet.create({
container: {
...Layout.containerWithPadding,
},
});

export default styles;
61 changes: 61 additions & 0 deletions app/screens/Languages/LanguagesScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { useCallback, useContext } from "react";
import { View, ScrollView } from "react-native";
import { useDispatch } from "react-redux";

import { SelectableListItem } from "components";
import { userPreferences } from "ducks";
import { LocalizationContext } from "utils";

import SupportedLanguages from "./SupportedLanguages";
import navigationOptions from "./LanguagesScreen.navigationOptions";
import styles from "./LanguagesScreen.styles";

const Language: React.FC<{
selectedLanguage: string;
language: string;
onSelectLanguage: (language: string) => void;
}> = ({ language, onSelectLanguage, selectedLanguage }) => {
const onClickLanguage = useCallback(() => {
onSelectLanguage?.(language);
}, [language, onSelectLanguage]);

return (
<SelectableListItem
key={language}
selected={selectedLanguage === language}
title={SupportedLanguages[language]}
onPress={onClickLanguage}
/>
);
};

const LanguagesScreen = () => {
const dispatch = useDispatch();
const { language, setLanguage } = useContext(LocalizationContext);
const onPress = useCallback(
(lang: string) => {
dispatch(userPreferences.actions.changeLanguage(lang));
setLanguage(lang);
},
[dispatch, setLanguage]
);

return (
<View style={styles.container}>
<ScrollView>
{Object.keys(SupportedLanguages).map((lang: string) => (
<Language
key={lang}
selectedLanguage={language}
language={lang}
onSelectLanguage={onPress}
/>
))}
</ScrollView>
</View>
);
};

LanguagesScreen.navigationOptions = navigationOptions;

export default LanguagesScreen;
13 changes: 13 additions & 0 deletions app/screens/Languages/SupportedLanguages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const SupportedLanguages = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change to supportedLanguages instead since it's not a class/component but just a static list. Could you move this file to utils/translations? And change it the following :

import { locale } from "expo-localization";

const supportedLanguages = { ... }

const currentLanguage = Object.keys(supportedLanguages).includes(locale.substr(0, 2))
  ? locale.substr(0, 2)
  : supportedLanguages[0];

export { currentLanguage, supportedLanguages };

// Key-value pairs
// State the name of the language in its own language :)
en: "English",
fr: "Français",
de: "Deutsche",
sv: "Slovenščina",
ru: "Pусский",
pt: "Português",
pl: "Polskie",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are missing 2 languages : Swedish & Danish (and maybe chinese which might come soon as well depending on who merge first).

};

export default SupportedLanguages;
11 changes: 11 additions & 0 deletions app/screens/Languages/__tests__/LanguagesScreen.tests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";
import { create } from "react-test-renderer";

import LanguagesScreen from "../LanguagesScreen";

// jest.unmock("../");

it("LanguageScreen renders correctly", () => {
const tree = create(<LanguagesScreen />).toJSON();
expect(tree).toMatchSnapshot();
});
Loading