From ff828b066031c30bf48d8088087b536e48690d76 Mon Sep 17 00:00:00 2001 From: Thibault Reidy <147397675+ReidyT@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:21:46 +0100 Subject: [PATCH] feat: add translations (#60) --- src/config/buildingMaterials.ts | 10 +- src/config/i18n.ts | 3 +- src/langs/constants.ts | 9 -- src/langs/en.json | 101 +++++++++++++++--- src/langs/fr.json | 6 +- src/modules/ErrorBoundary.tsx | 26 ++--- .../SimulationControlPanel/HouseControl.tsx | 30 ++++-- .../MaterialControlDialog.tsx | 31 ++++-- .../useMaterialControlDialog.tsx | 4 + .../SimulationControlPanel.tsx | 33 +++--- .../WindowControlDialog.tsx | 41 +++++-- .../SimulationInformations.tsx | 20 ++-- 12 files changed, 218 insertions(+), 96 deletions(-) delete mode 100644 src/langs/constants.ts diff --git a/src/config/buildingMaterials.ts b/src/config/buildingMaterials.ts index 74ac003..66b5a34 100644 --- a/src/config/buildingMaterials.ts +++ b/src/config/buildingMaterials.ts @@ -8,19 +8,19 @@ export const BUILDING_MATERIALS = { thickness: 0.16, }), FiberGlass: BuildingMaterial.create({ - name: 'Fiber Glass', + name: 'FiberGlass', price: 3_000, thermalConductivity: 0.115, thickness: 0.16, }), XPSFoam: BuildingMaterial.create({ - name: 'XPS Foam', + name: 'XPSFoam', price: 10, thermalConductivity: 0.024, thickness: 0.16, }), MineralWool: BuildingMaterial.create({ - name: 'Mineral Wool', + name: 'MineralWool', price: 7, thermalConductivity: 0.03, thickness: 0.16, @@ -32,7 +32,7 @@ export const BUILDING_MATERIALS = { thickness: 0.2, }), WindowGlass: BuildingMaterial.create({ - name: 'Window Glass', + name: 'WindowGlass', price: 150, thermalConductivity: 0.8, thickness: 0.004, @@ -50,3 +50,5 @@ export const BUILDING_MATERIALS = { thickness: 0.2, }), } as const; + +export type BuildingMaterialKeys = keyof typeof BUILDING_MATERIALS; diff --git a/src/config/i18n.ts b/src/config/i18n.ts index 015e49a..5b090ae 100644 --- a/src/config/i18n.ts +++ b/src/config/i18n.ts @@ -6,7 +6,7 @@ import en from '../langs/en.json'; import fr from '../langs/fr.json'; export const DEFAULT_LANGUAGE = 'en'; -export const defaultNS = 'translations'; +export const defaultNS = 'common'; export const resources = { en, fr, @@ -27,7 +27,6 @@ i18n.use(initReactI18next).init({ debug: import.meta.env.DEV, ns: [defaultNS], defaultNS, - keySeparator: false, interpolation: { escapeValue: false, formatSeparator: ',', diff --git a/src/langs/constants.ts b/src/langs/constants.ts deleted file mode 100644 index 898e6bc..0000000 --- a/src/langs/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TimeUnit } from '@/types/time'; - -export const TRANSLATIONS = { - [TimeUnit.Years]: 'YEARS', - [TimeUnit.Months]: 'MONTHS', - [TimeUnit.Weeks]: 'WEEKS', - [TimeUnit.Days]: 'DAYS', - [TimeUnit.Hours]: 'HOURS', -} as const; diff --git a/src/langs/en.json b/src/langs/en.json index 6d2739b..84d0636 100644 --- a/src/langs/en.json +++ b/src/langs/en.json @@ -1,6 +1,5 @@ { - "translations": { - "Welcome to the Graasp App Starter Kit": "Welcome to the Graasp App Starter Kit", + "DATES": { "YEARS_one": "{{count}} year", "YEARS_other": "{{count}} years", "MONTHS_one": "{{count}} month", @@ -10,21 +9,91 @@ "DAYS_one": "{{count}} day", "DAYS_other": "{{count}} days", "HOURS_one": "{{count}} hour", - "HOURS_other": "{{count}} hours", - "ERROR_BOUNDARY": { - "FALLBACK": { - "MESSAGE_TITLE": "Sorry, something went wrong with this application", - "MESSAGE_FEEDBACK": "Our team has been notified. If you would like to help, please, tell us what happened below.", - "ERROR_DETAILS": "Details of the error", - "NAME_LABEL": "Name", - "NAME_HELPER": "Provide your name (optional)", - "EMAIL_LABEL": "Email", - "EMAIL_HELPER": "Provide your email (optional)", - "COMMENT_LABEL": "Comment", - "COMMENT_HELPER": "Tell us what happened (optional)", - "THANKS_FOR_FEEDBACK": "Thank you for your feedback!", - "SEND": "Send your feedback" + "HOURS_other": "{{count}} hours" + }, + "SEASONS": { + "Winter": "Winter", + "Spring": "Spring", + "Summer": "Summer", + "Autumn": "Autumn" + }, + "SIMULATION_INFORMATIONS": { + "CURRENT_PERIOD": { + "OUTDOOR": "Outdoor", + "INDOOR": "Indoor", + "HEAT_LOSS": "Heat Loss" + }, + "TOTAL": { + "TITLE": "Total", + "HEAT_LOSS": "Heat Loss", + "ELECTRICITY_COST": "Electricity Cost", + "HOUSE_WALL_SIZE": "House Wall" + } + }, + "SIMULATION_CONTROL_PANEL": { + "HOUSE_CONTROL_PANEL": { + "TITLE": "House", + "WALL_INSULATION_SELECT_LABEL": "Wall Insulation", + "WINDOW_INSULATION_SELECT_LABEL": "Windows Insulation", + "MATERIAL_DIALOG": { + "TITLE": "Configure the {{insulation}} insulation", + "PRICE_LABEL": "Price", + "THICKNESS_LABEL": "Thickness", + "THERMAL_CONDUCTIVITY_LABEL": "Thermal Conductivity", + "CLOSE_BUTTON": "Close" + }, + + "WINDOW_DIALOG": { + "TITLE": "Configure the {{window_insulation}} windows", + "SIZE_LABEL": "Window Size", + "CURRENT_SIZE_LABEL": "Dimensions", + "Small": "Small", + "Medium": "Medium", + "Large": "Large", + "WINDOW_COPOSITION_TABLE": { + "LABEL": "Composition of the {{window_insulation}} windows", + "NAME_HEADER": "Name", + "THICKNESS_HEADER": "Thickness", + "THERMAL_CONDUCTIVITY_HEADER": "Thermal Conductivity" + }, + "CLOSE_BUTTON": "Close" } } + }, + "INSULATIONS": { + "Brick": "Brick", + "Aerogel": "Aerogel", + "Fiberglass": "Fiberglass", + "XPSFoam": "XPS Foam", + "MineralWool": "Mineral Wool", + "Wood": "Wood", + "SinglePane": "Single Pane", + "DoublePane": "Double Pane", + "TriplePane": "Triple Pane" + }, + "MATERIALS": { + "Aerogel": "Aerogel", + "FiberGlass": "Fiberglass", + "XPSFoam": "XPS Foam", + "MineralWool": "Mineral Wool", + "Brick": "Brick", + "WindowGlass": "Window Glass", + "Argon": "Argon", + "Wood": "Wood" + }, + "ERROR_BOUNDARY": { + "FALLBACK": { + "MESSAGE_TITLE": "Sorry, something went wrong with this application", + "MESSAGE_FEEDBACK": "Our team has been notified. If you would like to help, please, tell us what happened below.", + "ERROR_DETAILS": "Details of the error", + "NAME_LABEL": "Name", + "NAME_HELPER": "Provide your name (optional)", + "EMAIL_LABEL": "Email", + "EMAIL_HELPER": "Provide your email (optional)", + "COMMENT_LABEL": "Comment", + "COMMENT_HELPER": "Tell us what happened (optional)", + "THANKS_FOR_FEEDBACK": "Thank you for your feedback!", + "SEND": "Send your feedback" + } } } diff --git a/src/langs/fr.json b/src/langs/fr.json index f9e97c8..0967ef4 100644 --- a/src/langs/fr.json +++ b/src/langs/fr.json @@ -1,5 +1 @@ -{ - "translations": { - "Welcome to the Graasp App Starter Kit": "Bienvenue dans le kit de démarrage de l'application Graasp" - } -} +{} diff --git a/src/modules/ErrorBoundary.tsx b/src/modules/ErrorBoundary.tsx index d22f6ca..82fbe57 100644 --- a/src/modules/ErrorBoundary.tsx +++ b/src/modules/ErrorBoundary.tsx @@ -6,8 +6,8 @@ import { ErrorFallback } from '@graasp/ui/apps'; import * as Sentry from '@sentry/react'; const ErrorBoundary: FC<{ children?: ReactNode }> = ({ children }) => { - const { t: tFallback } = useTranslation('translations', { - keyPrefix: 'ERROR_BOUNDARY.FALLBACK', + const { t } = useTranslation('ERROR_BOUNDARY', { + keyPrefix: 'FALLBACK', }); return ( = ({ children }) => { componentStack={componentStack} eventId={eventId} captureUserFeedback={Sentry.captureUserFeedback} - title={tFallback('MESSAGE_TITLE')} - formTitle={tFallback('MESSAGE_FEEDBACK')} - nameLabel={tFallback('NAME_LABEL')} - nameHelper={tFallback('NAME_HELPER')} - emailLabel={tFallback('EMAIL_LABEL')} - emailHelper={tFallback('EMAIL_HELPER')} - commentLabel={tFallback('COMMENT_LABEL')} - commentHelper={tFallback('COMMENT_HELPER')} - thanksMessage={tFallback('THANKS_FOR_FEEDBACK')} - sendButtonLabel={tFallback('SEND')} - errorDetailsLabel={tFallback('ERROR_DETAILS')} + title={t('MESSAGE_TITLE')} + formTitle={t('MESSAGE_FEEDBACK')} + nameLabel={t('NAME_LABEL')} + nameHelper={t('NAME_HELPER')} + emailLabel={t('EMAIL_LABEL')} + emailHelper={t('EMAIL_HELPER')} + commentLabel={t('COMMENT_LABEL')} + commentHelper={t('COMMENT_HELPER')} + thanksMessage={t('THANKS_FOR_FEEDBACK')} + sendButtonLabel={t('SEND')} + errorDetailsLabel={t('ERROR_DETAILS')} /> )} > diff --git a/src/modules/scenes/SimulationControlPanel/HouseControl.tsx b/src/modules/scenes/SimulationControlPanel/HouseControl.tsx index c5d5dae..2dfa903 100644 --- a/src/modules/scenes/SimulationControlPanel/HouseControl.tsx +++ b/src/modules/scenes/SimulationControlPanel/HouseControl.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from 'react-i18next'; + import { FormControl, IconButton, @@ -25,8 +27,18 @@ import { MaterialControlDialog } from './MaterialControlDialog/MaterialControlDi import { WindowControlDialog } from './WindowControlDialog/WindowControlDialog'; export const HouseControl = (): JSX.Element => { + const { t } = useTranslation('SIMULATION_CONTROL_PANEL'); + const { t: tInsulations } = useTranslation('INSULATIONS'); const { changeComponentInsulation } = useHouseComponents(); + const wallInsulations = Object.keys( + HOUSE_INSULATIONS.Wall, + ) as (keyof typeof HOUSE_INSULATIONS.Wall)[]; + + const windowInsulations = Object.keys( + HOUSE_INSULATIONS.Window, + ) as (keyof typeof HOUSE_INSULATIONS.Window)[]; + const { open: openMaterials, handleOpen: handleOpenMaterials, @@ -67,19 +79,21 @@ export const HouseControl = (): JSX.Element => { - Material + + {t('HOUSE_CONTROL_PANEL.WALL_INSULATION_SELECT_LABEL')} + @@ -97,20 +111,20 @@ export const HouseControl = (): JSX.Element => { - Window Insulation + {t('HOUSE_CONTROL_PANEL.WINDOW_INSULATION_SELECT_LABEL')} diff --git a/src/modules/scenes/SimulationControlPanel/MaterialControlDialog/MaterialControlDialog.tsx b/src/modules/scenes/SimulationControlPanel/MaterialControlDialog/MaterialControlDialog.tsx index 298fe45..f882c91 100644 --- a/src/modules/scenes/SimulationControlPanel/MaterialControlDialog/MaterialControlDialog.tsx +++ b/src/modules/scenes/SimulationControlPanel/MaterialControlDialog/MaterialControlDialog.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from 'react-i18next'; + import { TabContext, TabList, TabPanel } from '@mui/lab'; import { FormControl, @@ -14,6 +16,8 @@ import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; +import { BuildingMaterialKeys } from '@/config/buildingMaterials'; + import { FormControlValidator } from './FormControlValidator'; import { useMaterialControlDialog } from './useMaterialControlDialog'; @@ -26,14 +30,23 @@ export const MaterialControlDialog = ({ open, handleClose, }: Props): JSX.Element => { + const { t } = useTranslation('SIMULATION_CONTROL_PANEL', { + keyPrefix: 'HOUSE_CONTROL_PANEL.MATERIAL_DIALOG', + }); + const { t: tMaterials } = useTranslation('MATERIALS'); + const { t: tInsulations } = useTranslation('INSULATIONS'); + const { currTab, updateTab, wallMaterials, + wallInsulation, handleThicknessChange, handlePriceChange, } = useMaterialControlDialog(); + const insulationName = wallInsulation ? tInsulations(wallInsulation) : ''; + return ( - House Wall Materials + {t('TITLE', { insulation: insulationName })} @@ -50,7 +63,11 @@ export const MaterialControlDialog = ({ updateTab(v)}> {wallMaterials?.map((w) => ( - + ))} @@ -59,7 +76,7 @@ export const MaterialControlDialog = ({ handlePriceChange(w.name, Number.parseFloat(newValue)) @@ -79,7 +96,7 @@ export const MaterialControlDialog = ({ /> handleThicknessChange( @@ -107,13 +124,13 @@ export const MaterialControlDialog = ({ - Thermal Conductivity + {t('THERMAL_CONDUCTIVITY_LABEL')} W/m·K } @@ -126,7 +143,7 @@ export const MaterialControlDialog = ({ - + ); diff --git a/src/modules/scenes/SimulationControlPanel/MaterialControlDialog/useMaterialControlDialog.tsx b/src/modules/scenes/SimulationControlPanel/MaterialControlDialog/useMaterialControlDialog.tsx index 7ab3e0f..699b4a7 100644 --- a/src/modules/scenes/SimulationControlPanel/MaterialControlDialog/useMaterialControlDialog.tsx +++ b/src/modules/scenes/SimulationControlPanel/MaterialControlDialog/useMaterialControlDialog.tsx @@ -1,5 +1,6 @@ import { useEffect, useMemo, useState } from 'react'; +import { HouseInsulation } from '@/config/houseInsulations'; import { useHouseComponents } from '@/context/HouseComponentsContext'; import { BuildingMaterial } from '@/models/BuildingMaterial'; import { HouseComponent } from '@/types/houseComponent'; @@ -11,6 +12,7 @@ type UseMaterialControlDialogReturnType = { handleThicknessChange: (materialName: string, newThickness: number) => void; handlePriceChange: (materialName: string, newPrice: number) => void; wallMaterials?: NonEmptyArray; + wallInsulation?: HouseInsulation; }; export const useMaterialControlDialog = @@ -24,6 +26,7 @@ export const useMaterialControlDialog = [houseComponentsConfigurator], ); const wallMaterials = wallComponents?.buildingMaterials; + const wallInsulation = wallComponents?.insulationName; useEffect(() => { if ( @@ -63,6 +66,7 @@ export const useMaterialControlDialog = return { currTab, wallMaterials, + wallInsulation, updateTab: (tab) => setCurrTab(tab), handleThicknessChange, handlePriceChange, diff --git a/src/modules/scenes/SimulationControlPanel/SimulationControlPanel.tsx b/src/modules/scenes/SimulationControlPanel/SimulationControlPanel.tsx index 3983ad9..b1eb7e0 100644 --- a/src/modules/scenes/SimulationControlPanel/SimulationControlPanel.tsx +++ b/src/modules/scenes/SimulationControlPanel/SimulationControlPanel.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from 'react-i18next'; + import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import Accordion from '@mui/material/Accordion'; import AccordionDetails from '@mui/material/AccordionDetails'; @@ -5,17 +7,20 @@ import AccordionSummary from '@mui/material/AccordionSummary'; import { HouseControl } from './HouseControl'; -export const SimulationControlPanel = (): JSX.Element => ( - - } - aria-controls="panel-house-content" - id="panel-house-header" - > - House - - - - - -); +export const SimulationControlPanel = (): JSX.Element => { + const { t } = useTranslation('SIMULATION_CONTROL_PANEL'); + return ( + + } + aria-controls="panel-house-content" + id="panel-house-header" + > + {t('HOUSE_CONTROL_PANEL.TITLE')} + + + + + + ); +}; diff --git a/src/modules/scenes/SimulationControlPanel/WindowControlDialog/WindowControlDialog.tsx b/src/modules/scenes/SimulationControlPanel/WindowControlDialog/WindowControlDialog.tsx index 2aa5644..41fff34 100644 --- a/src/modules/scenes/SimulationControlPanel/WindowControlDialog/WindowControlDialog.tsx +++ b/src/modules/scenes/SimulationControlPanel/WindowControlDialog/WindowControlDialog.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from 'react-i18next'; + import { FormControl, FormHelperText, @@ -38,6 +40,11 @@ export const WindowControlDialog = ({ open, handleClose, }: Props): JSX.Element | null => { + const { t } = useTranslation('SIMULATION_CONTROL_PANEL', { + keyPrefix: 'HOUSE_CONTROL_PANEL.WINDOW_DIALOG', + }); + const { t: tInsulations } = useTranslation('INSULATIONS'); + const { changeWindowSize, windowSize } = useWindowSize(); const { houseComponentsConfigurator } = useHouseComponents(); const windowComponent = houseComponentsConfigurator.getFirstOfType( @@ -48,6 +55,8 @@ export const WindowControlDialog = ({ return null; } + const windowInsulation = tInsulations(windowComponent.insulationName); + const handleSizeChange = (newSize: string): void => { changeWindowSize(newSize as WindowSizeType); }; @@ -59,27 +68,31 @@ export const WindowControlDialog = ({ aria-labelledby="alert-dialog-title-window" aria-describedby="alert-dialog-description-window" > - House Windows + + {t('TITLE', { window_insulation: windowInsulation })} + - Window Size + + {t('SIZE_LABEL')} + - Dimensions:{' '} + {t('CURRENT_SIZE_LABEL')}{' '} {formatComponentSize({ componentSize: windowComponent.size })} @@ -87,13 +100,21 @@ export const WindowControlDialog = ({ - Name - Tickness - Thermal Conductivity + + {t('WINDOW_COPOSITION_TABLE.NAME_HEADER')} + + + {t('WINDOW_COPOSITION_TABLE.THICKNESS_HEADER')} + + + {t('WINDOW_COPOSITION_TABLE.THERMAL_CONDUCTIVITY_HEADER')} + @@ -115,7 +136,7 @@ export const WindowControlDialog = ({ - + ); diff --git a/src/modules/scenes/SimulationInformations/SimulationInformations.tsx b/src/modules/scenes/SimulationInformations/SimulationInformations.tsx index e4ab12d..b4ee80a 100644 --- a/src/modules/scenes/SimulationInformations/SimulationInformations.tsx +++ b/src/modules/scenes/SimulationInformations/SimulationInformations.tsx @@ -1,3 +1,5 @@ +import { useTranslation } from 'react-i18next'; + import { Divider, Stack, Typography } from '@mui/material'; import { @@ -15,6 +17,8 @@ import { useSimulation } from '@/context/SimulationContext'; import { useSimulationInformations } from './useSimulationInformations'; export const SimulationInformations = (): JSX.Element => { + const { t: tSeasons } = useTranslation('SEASONS'); + const { t: tInformations } = useTranslation('SIMULATION_INFORMATIONS'); const { season } = useSeason(); const { @@ -49,24 +53,24 @@ export const SimulationInformations = (): JSX.Element => { {seasonIcon} - {season} + {tSeasons(season)} - Outdoor + {tInformations('CURRENT_PERIOD.OUTDOOR')} {outdoorTemperature} °C - Indoor + {tInformations('CURRENT_PERIOD.INDOOR')} {indoorTemperature} °C - Heat Loss + {tInformations('CURRENT_PERIOD.HEAT_LOSS')} {heatLoss.value} {heatLoss.unit} @@ -78,13 +82,13 @@ export const SimulationInformations = (): JSX.Element => { - Total + {tInformations('TOTAL.TITLE')} - Heat Loss + {tInformations('TOTAL.HEAT_LOSS')} {totalHeatLoss.value} {totalHeatLoss.unit} @@ -92,14 +96,14 @@ export const SimulationInformations = (): JSX.Element => { - Electricity Cost + {tInformations('TOTAL.ELECTRICITY_COST')} {electricityCost} CHF - House Walls + {tInformations('TOTAL.HOUSE_WALL_SIZE')} {formattedWallSize}
- Composition of the {windowComponent.insulationName} Windows + {t('WINDOW_COPOSITION_TABLE.LABEL', { + window_insulation: windowInsulation, + })}