From 8546c863327787b62c6f48505b67a745c19cf384 Mon Sep 17 00:00:00 2001 From: Arthur Jamet <60505370+Arthi-chaud@users.noreply.github.com> Date: Thu, 24 Nov 2022 17:20:45 +0000 Subject: [PATCH] Front/translate refactor (#110) * Front: i18n: Create component * Front: Use new translation component * Front: Translation COmpoent: Change props name * Front: Fix merge --- front/components/SongCardGrid.tsx | 2 +- front/components/Translate.tsx | 20 +++++++++++ front/components/forms/signinform.tsx | 12 ++++--- front/components/forms/signupform.tsx | 20 +++++++---- front/i18n/i18n.ts | 11 ++++-- front/models/User.ts | 2 +- front/views/AuthenticationView.tsx | 7 ++-- front/views/HomeView.tsx | 30 ++++++++++------ front/views/SettingsView.tsx | 51 +++++++++++++++------------ front/views/SongLobbyView.tsx | 25 ++++++++----- 10 files changed, 124 insertions(+), 56 deletions(-) create mode 100644 front/components/Translate.tsx diff --git a/front/components/SongCardGrid.tsx b/front/components/SongCardGrid.tsx index 89ffeb7..7f816ea 100644 --- a/front/components/SongCardGrid.tsx +++ b/front/components/SongCardGrid.tsx @@ -6,7 +6,7 @@ import { Heading, VStack } from 'native-base'; type SongCardGrid = { songs: Parameters[0][]; maxItemPerRow?: number, - heading?: string + heading?: JSX.Element, } const SongCardGrid = (props: SongCardGrid) => { diff --git a/front/components/Translate.tsx b/front/components/Translate.tsx new file mode 100644 index 0000000..ef6da7c --- /dev/null +++ b/front/components/Translate.tsx @@ -0,0 +1,20 @@ +import { translate } from "../i18n/i18n"; +import { en } from "../i18n/Translations"; +import { RootState, useSelector } from "../state/Store"; + +type TranslateProps = { + translationKey: keyof typeof en; + format?: (translated: string) => string; +} +/** + * Translation component + * @param param0 + * @returns + */ +const Translate = ({ translationKey, format }: TranslateProps) => { + const selectedLanguage = useSelector((state: RootState) => state.language.value); + const translated = translate(translationKey, selectedLanguage); + return <>{format ? format(translated) : translated}; +} + +export default Translate; \ No newline at end of file diff --git a/front/components/forms/signinform.tsx b/front/components/forms/signinform.tsx index 24aa310..1b3005a 100644 --- a/front/components/forms/signinform.tsx +++ b/front/components/forms/signinform.tsx @@ -1,7 +1,7 @@ // a form for sign in import React from "react"; -import { translate } from "../../i18n/i18n"; +import { Translate, translate } from "../../i18n/i18n"; import { string } from "yup"; import { FormControl, @@ -51,7 +51,9 @@ const LoginForm = ({ onSubmit }: SigninFormProps) => { formData.password.error !== null } > - {translate("username")} + + + { > {formData.username.error} - {translate("password")} + + + { } }} > - {translate("login")} + diff --git a/front/components/forms/signupform.tsx b/front/components/forms/signupform.tsx index f04f6c9..2b1c0db 100644 --- a/front/components/forms/signupform.tsx +++ b/front/components/forms/signupform.tsx @@ -1,7 +1,7 @@ // a form for sign up import React from "react"; -import { translate } from "../../i18n/i18n"; +import { Translate, translate } from "../../i18n/i18n"; import { string } from "yup"; import { FormControl, @@ -74,7 +74,9 @@ const LoginForm = ({ onSubmit }: SignupFormProps) => { formData.email.error !== null } > - {translate("username")} + + + { > {formData.username.error} - {translate("email")} + + + { > {formData.email.error} - {translate("password")} + + + { > {formData.password.error} - {translate("repeatPassword")} + + + { } }} > - {translate("signUp")} + diff --git a/front/i18n/i18n.ts b/front/i18n/i18n.ts index 132ca3d..7cbf9f5 100644 --- a/front/i18n/i18n.ts +++ b/front/i18n/i18n.ts @@ -1,7 +1,7 @@ import { en, fr, sp } from './Translations'; import i18n from "i18next"; import { initReactI18next } from "react-i18next"; - +import Translate from '../components/Translate'; export type AvailableLanguages = 'en' | 'fr' | 'sp'; export const DefaultLanguage: AvailableLanguages = 'en'; @@ -28,9 +28,16 @@ i18n }); export default i18n; + /** * Typesafe translation method * @param textKey the key of th text to translate * @returns the translated text */ -export const translate = (textKey: keyof typeof en) => i18n.t(textKey); \ No newline at end of file +export const translate = (key: keyof typeof en, language?: AvailableLanguages) => { + return i18n.t(key, { + lng: language + }); +} + +export { Translate }; \ No newline at end of file diff --git a/front/models/User.ts b/front/models/User.ts index c7b5a94..aaf5e28 100644 --- a/front/models/User.ts +++ b/front/models/User.ts @@ -3,7 +3,7 @@ import Model from "./Model"; import UserSettings from "./UserSettings"; interface User extends Model { - username: string; + name: string; email: string; xp: number; premium: boolean; diff --git a/front/views/AuthenticationView.tsx b/front/views/AuthenticationView.tsx index ffd78bb..29c89c8 100644 --- a/front/views/AuthenticationView.tsx +++ b/front/views/AuthenticationView.tsx @@ -1,6 +1,6 @@ import React from "react"; import { useDispatch } from '../state/Store'; -import { translate } from "../i18n/i18n"; +import { Translate, translate } from "../i18n/i18n"; import API from "../API"; import { setAccessToken } from "../state/UserSlice"; import { Center, Button, Text } from 'native-base'; @@ -33,13 +33,16 @@ const AuthenticationView = () => { return (
+ {mode === "signin" ? hanldeSignin(username, password, (accessToken) => dispatch(setAccessToken(accessToken)))} /> : handleSignup(username, password, email, (accessToken) => dispatch(setAccessToken(accessToken)))} /> } { mode ==="signin" && }
); diff --git a/front/views/HomeView.tsx b/front/views/HomeView.tsx index faefc22..6ee0a30 100644 --- a/front/views/HomeView.tsx +++ b/front/views/HomeView.tsx @@ -6,7 +6,7 @@ import { Box, ScrollView, Flex, useBreakpointValue, Text, VStack, Progress, Butt import { useNavigation } from "@react-navigation/native"; import SongCardGrid from '../components/SongCardGrid'; import CompetenciesTable from '../components/CompetenciesTable' -import { translate } from "../i18n/i18n"; +import { Translate } from "../i18n/i18n"; const ProgressBar = ({ xp }: { xp: number}) => { const level = Math.floor(xp / 1000); @@ -16,11 +16,17 @@ const ProgressBar = ({ xp }: { xp: number}) => { return ( - {`${translate('level')} ${level}`} + + `${id} ${level}`}/> + - {xp} / {nextLevelThreshold} {translate('levelProgress')} + + `${xp} / ${nextLevelThreshold} ${levelProgress}`} + /> + ); } @@ -46,7 +52,9 @@ const HomeView = () => { return - {`${translate('welcome')} ${userQuery.data.username}!`} + + `${welcome} ${userQuery.data.name}!`}/> + @@ -55,7 +63,7 @@ const HomeView = () => { } songs={nextStepQuery.data?.filter((song) => artistsQueries.find((artistQuery) => artistQuery.data?.id === song.artistId)) .map((song) => ({ albumCover: song.cover, @@ -68,7 +76,7 @@ const HomeView = () => { - {translate('mySkillsToImprove')} + @@ -76,7 +84,7 @@ const HomeView = () => { } maxItemPerRow={2} songs={playHistoryQuery.data?.filter((song) => artistsQueries.find((artistQuery) => artistQuery.data?.id === song.artistId)) .map((song) => ({ @@ -96,11 +104,11 @@ const HomeView = () => { - + } songs={searchHistoryQuery.data?.filter((song) => artistsQueries.find((artistQuery) => artistQuery.data?.id === song.artistId)) .map((song) => ({ albumCover: song.cover, @@ -113,7 +121,9 @@ const HomeView = () => { - + diff --git a/front/views/SettingsView.tsx b/front/views/SettingsView.tsx index 4ff0a37..b6facef 100644 --- a/front/views/SettingsView.tsx +++ b/front/views/SettingsView.tsx @@ -6,7 +6,7 @@ import { unsetAccessToken } from '../state/UserSlice'; import { useDispatch } from "react-redux"; import { useSelector } from '../state/Store'; import { useLanguage } from "../state/LanguageSlice"; -import i18n, { AvailableLanguages, DefaultLanguage, translate } from "../i18n/i18n"; +import { AvailableLanguages, DefaultLanguage, translate, Translate } from "../i18n/i18n"; const SettingsStack = createNativeStackNavigator(); @@ -16,31 +16,31 @@ const MainView = ({navigation}) => { return (
) @@ -52,9 +52,12 @@ const PreferencesView = ({navigation}) => { return (
- { translate('prefBtn')} - - + + + + @@ -131,24 +134,24 @@ const NotificationsView = ({navigation}) => { return (
- { translate('notifBtn')} - - + + + + Push notifications - Email notifications - Training reminder - New songs @@ -160,9 +163,13 @@ const NotificationsView = ({navigation}) => { const PrivacyView = ({navigation}) => { return (
- { translate('privBtn')} + + + - + Data Collection diff --git a/front/views/SongLobbyView.tsx b/front/views/SongLobbyView.tsx index 5906aa6..088ae39 100644 --- a/front/views/SongLobbyView.tsx +++ b/front/views/SongLobbyView.tsx @@ -3,10 +3,9 @@ import { Button, Divider, Box, Center, Image, Text, VStack, PresenceTransition, import { useQuery } from 'react-query'; import LoadingComponent from "../components/Loading"; import React, { useEffect, useState } from "react"; -import { translate } from "../i18n/i18n"; +import { Translate, translate } from "../i18n/i18n"; import formatDuration from "format-duration"; import { Ionicons } from '@expo/vector-icons'; -import { useSelector } from "../state/Store"; import API from "../API"; interface SongLobbyProps { @@ -39,19 +38,29 @@ const SongLobbyView = () => { - {songQuery.data!.name} - {'3:20'} - {translate('level')} { chaptersQuery.data!.reduce((a, b) => a + b.difficulty, 0) / chaptersQuery.data!.length } - + {songQuery.data!.title} + + `3:20 - ${level} - ${ chaptersQuery.data!.reduce((a, b) => a + b.difficulty, 0) / chaptersQuery.data!.length }`} + /> + + - {translate('bestScore') } + + + {scoresQuery.data!.sort()[0]?.score} - {translate('lastScore') } + + + {scoresQuery.data!.slice(-1)[0]!.score} @@ -62,7 +71,7 @@ const SongLobbyView = () => { onPress={() => setChaptersOpen(!chaptersOpen)} endIcon={} > - {translate('chapters')} +