diff --git a/src/components/ResponsiveAppBar.jsx b/src/components/ResponsiveAppBar.jsx index 07da99c1ce..736a4f506f 100644 --- a/src/components/ResponsiveAppBar.jsx +++ b/src/components/ResponsiveAppBar.jsx @@ -62,10 +62,14 @@ const pages = [ }, ], }, + { + url: "nutrition", + translationKey: "menu.nutritions", + desktopOnly: true, + }, { translationKey: "menu.manage" }, { url: "insights", translationKey: "menu.insights", devModeOnly: true }, { url: "dashboard", translationKey: "menu.dashboard" }, - // { url: "nutriscore", translationKey: "menu.nutriscore", devModeOnly: true }, { url: "settings", translationKey: "menu.settings", mobileOnly: true }, ]; @@ -139,6 +143,9 @@ const ResponsiveAppBar = () => { if (page.mobileOnly) { return !isDesktop; } + if (page.desktopOnly) { + return isDesktop; + } return true; }; diff --git a/src/i18n/common.json b/src/i18n/common.json index 76aac47537..1c4651670e 100644 --- a/src/i18n/common.json +++ b/src/i18n/common.json @@ -70,6 +70,7 @@ "countries": "Countries", "view": "View", "edit": "Edit", + "remaining": "Remaining: {{count}}", "hide_images": "Hide images (faster loading)", "display_images": "Display images", "annotations": "Annotations", @@ -129,7 +130,7 @@ "logos-deep-search": "Infinite annotation", "manage": "Manage", "insights": "Insights", - "nutritions": "Nutrition facts", + "nutritions": "Nutrition", "settings": "Settings", "eco-score": "Eco-score", "dashboard": "Logos dashboard", @@ -247,7 +248,7 @@ "Open_Food_Facts_wiki": "Open Food Facts wiki (en)", "Translators": "Translators", "Partners": "Partners", - "Open_Beauty_Facts": "Open Beauty Facts - Cosmetics", + "Open_Beauty_Facts": "Open Beauty Facts - cosmetics", "text2": "A collaborative, free and open database of food products from around the world." }, "helper": { @@ -313,53 +314,34 @@ "close": "Close" }, "nutrition": { - "instructions": { - "cropTheTable": "Crop the image to simplify your annotation", - "verifyNutrimentDetected": "Indicate if the nutriment detected is correct", - "selectMissingNutriments": "Add missing nutriments (you can click even if the word has no box)", - "selectValue": "Select the nutritional value of: " - }, - "steps": { - "1": "Crop", - "2": "Special values", - "3": "Nutriments Selection", - "4": "Values Selection" - }, - "loading": "Loading...", - "unknown_brand": "Brand Unknown", - "value_missing": "You forgot to input a value.", - "next": "Next step", - "prev": "Prev step", - "skip": "Skip this product", - "validate": "Validate Table", - "unselect_picture": "No nutrition info", - "basis": "data for: ", - "servingSize": "serving size", - "value": "Value", - "unit": "Unit", - "nutriments": { - "nutriment_energy-kj": "Energy (kJ)", - "nutriment_energy-kcal": "Energy (kCal)", - "nutriment_fat": "Fat", - "nutriment_saturated-fat": "Saturated fat", - "nutriment_carbohydrates": "Carbohydrates", - "nutriment_sugars": "Sugar", - "nutriment_proteins": "Protein content", - "nutriment_fiber": "Fibres", - "nutriment_salt": "Salt", - "nutriment_sodium": "Sodium" + "table_partialy_filled": "Table with already some data", + "display_off_values": "Display values from OpenFoodFacts", + "nutrients": "Nutrients", + "serving_size": "Serving size", + "add_nutrient": "-- add nutrient --", + "validate_100g": "Validate (100g)", + "validate_serving": "Validate (serving)", + "skip": "Skip", + "invalid_image": "Invalid image", + "pictures": { + "picture_date": "Photo uploaded", + "selected_as_nutrients": "Selected as nutriment image ✅", + "not_selected_as_nutrients": "Not selected as nutriment ❌" }, - "history": { - "title": "Session History", - "status": "status", - "product name": "product name", - "view": "view page", - "edit": "edit page", - "SKIP": "ignored", - "VALIDATED": "edited", - "WRONG_IMAGE": "unselected picture", - "close": "close", - "open": "open history" + "instructions": { + "open_button": "Open instructions", + "title": "How to use the interface", + "chose_between_empty_and_partially_filled": "You can chose between products for which OpenFoodFacts (OFF) has no data about the nutrients. Or product with partially filled nutrient table.", + "picture_date_is_display": "The date of the picture is displayed on top of it. Allowing you to check if it's a recent one.", + "nutriments_from_off_are_displayed": "You can display data that are already present in the OFF database. They will appear under the inputs:", + "green_for_same": "With <1>green for those matching with the input value.", + "orange_for_empty": "With <1>orange for those with empty input value.", + "red_for_different": "With <1>red for those with different input value.", + "indicate_not_provided_value": "Empty inputs are ignored. If fibers are not present in the table, you can put \"-\" to specify the value is not available.", + "validate_one_column": "Once you've verified one column, you can validate. It's useless to fill both columns. The data base only save one of them. The second one is computed by using proportionality.", + "skip_button": "If you don't know how to deal with an image, or you just don't want to do it, press \"skip\". Other user will be able to handle it.", + "invalid_image_button": "If the pictures is not a nutritional table, press \"Invalid image\" to remove it from the list of the images to verify", + "close": "Close" } }, "notfound": { diff --git a/src/i18n/en.json b/src/i18n/en.json index 38e97a2f8f..1c4651670e 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -70,6 +70,7 @@ "countries": "Countries", "view": "View", "edit": "Edit", + "remaining": "Remaining: {{count}}", "hide_images": "Hide images (faster loading)", "display_images": "Display images", "annotations": "Annotations", @@ -129,7 +130,7 @@ "logos-deep-search": "Infinite annotation", "manage": "Manage", "insights": "Insights", - "nutritions": "Nutrition facts", + "nutritions": "Nutrition", "settings": "Settings", "eco-score": "Eco-score", "dashboard": "Logos dashboard", @@ -313,53 +314,34 @@ "close": "Close" }, "nutrition": { - "instructions": { - "cropTheTable": "Crop the image to simplify your annotation", - "verifyNutrimentDetected": "Indicate if the nutriment detected is correct", - "selectMissingNutriments": "Add missing nutriments (you can click even if the word has no box)", - "selectValue": "Select the nutritional value of: " - }, - "steps": { - "1": "Crop", - "2": "Special values", - "3": "Nutriments Selection", - "4": "Values Selection" - }, - "loading": "Loading...", - "unknown_brand": "Brand Unknown", - "value_missing": "You forgot to input a value.", - "next": "Next step", - "prev": "Prev step", - "skip": "Skip this product", - "validate": "Validate Table", - "unselect_picture": "No nutrition info", - "basis": "data for: ", - "servingSize": "serving size", - "value": "Value", - "unit": "Unit", - "nutriments": { - "nutriment_energy-kj": "Energy (kJ)", - "nutriment_energy-kcal": "Energy (kCal)", - "nutriment_fat": "Fat", - "nutriment_saturated-fat": "Saturated fat", - "nutriment_carbohydrates": "Carbohydrates", - "nutriment_sugars": "Sugar", - "nutriment_proteins": "Protein content", - "nutriment_fiber": "Fibres", - "nutriment_salt": "Salt", - "nutriment_sodium": "Sodium" + "table_partialy_filled": "Table with already some data", + "display_off_values": "Display values from OpenFoodFacts", + "nutrients": "Nutrients", + "serving_size": "Serving size", + "add_nutrient": "-- add nutrient --", + "validate_100g": "Validate (100g)", + "validate_serving": "Validate (serving)", + "skip": "Skip", + "invalid_image": "Invalid image", + "pictures": { + "picture_date": "Photo uploaded", + "selected_as_nutrients": "Selected as nutriment image ✅", + "not_selected_as_nutrients": "Not selected as nutriment ❌" }, - "history": { - "title": "Session History", - "status": "status", - "product name": "product name", - "view": "view page", - "edit": "edit page", - "SKIP": "ignored", - "VALIDATED": "edited", - "WRONG_IMAGE": "unselected picture", - "close": "close", - "open": "open history" + "instructions": { + "open_button": "Open instructions", + "title": "How to use the interface", + "chose_between_empty_and_partially_filled": "You can chose between products for which OpenFoodFacts (OFF) has no data about the nutrients. Or product with partially filled nutrient table.", + "picture_date_is_display": "The date of the picture is displayed on top of it. Allowing you to check if it's a recent one.", + "nutriments_from_off_are_displayed": "You can display data that are already present in the OFF database. They will appear under the inputs:", + "green_for_same": "With <1>green for those matching with the input value.", + "orange_for_empty": "With <1>orange for those with empty input value.", + "red_for_different": "With <1>red for those with different input value.", + "indicate_not_provided_value": "Empty inputs are ignored. If fibers are not present in the table, you can put \"-\" to specify the value is not available.", + "validate_one_column": "Once you've verified one column, you can validate. It's useless to fill both columns. The data base only save one of them. The second one is computed by using proportionality.", + "skip_button": "If you don't know how to deal with an image, or you just don't want to do it, press \"skip\". Other user will be able to handle it.", + "invalid_image_button": "If the pictures is not a nutritional table, press \"Invalid image\" to remove it from the list of the images to verify", + "close": "Close" } }, "notfound": { diff --git a/src/pages/nutrition/Instructions.tsx b/src/pages/nutrition/Instructions.tsx index beec4202a4..4ede31d720 100644 --- a/src/pages/nutrition/Instructions.tsx +++ b/src/pages/nutrition/Instructions.tsx @@ -7,8 +7,10 @@ import DialogContent from "@mui/material/DialogContent"; import DialogContentText from "@mui/material/DialogContentText"; import DialogTitle from "@mui/material/DialogTitle"; import Typography from "@mui/material/Typography"; +import { Trans, useTranslation } from "react-i18next"; export default function Instructions() { + const { t } = useTranslation(); const [open, setOpen] = React.useState(false); const handleClickOpen = () => { @@ -27,7 +29,7 @@ export default function Instructions() { onClick={handleClickOpen} sx={{ margin: 2 }} > - Instructions + {t("nutrition.instructions.open_button")} - Comment utiliser cette interface + {t("nutrition.instructions.title")} - Avec les checkbox du haut, vous pouvez choisir entre traiter des - tableux de produit sans tableau nutritionel. Ou traiter des - produits pour lequels robotoof à trouver des nutriments en plus. + {t( + "nutrition.instructions.chose_between_empty_and_partially_filled", + )} - La date de la photo est affichée au dessus de la photo. + {t("nutrition.instructions.picture_date_is_display")} - Les nutriments déjà associé au produit sont affiché en petit sous - les inputs. + {t("nutrition.instructions.nutriments_from_off_are_displayed")}
  1. - En vert pour ceux qui - matchent avec la valeur du champ + + With green for those + matching with the input value. +
  2. - En orange si le champ - est vide + + With orange for + those with empty input value. +
  3. - En rouge si le champ est - différent de la valeur connue. + + With red for those + with different input value. +
- La valeur "-" indique une valeur abscente du tableau nutritionel. - Particulierement utile pour les fibres qui sont souvent abscente. + {t("nutrition.instructions.indicate_not_provided_value")}
- Quand une colone a été vérifiée (celle pour 100g ou celle par - portion) il ne vous reste plus qu'à la valider pour passer à la - suite. Innutil de remplir les deux colones. Une seul des deux peut - être enregistrée par OFF. + {t("nutrition.instructions.validate_one_column")}
    -
  • - Le bouton "skip" passe à la suite. quelqu'un d'autre s'en - chargera. -
  • -
  • - Le bouton "invalid image" indique que la photo ne correspond pas - à un tableau nutritionel, et supprime la question pour tout le - monde. -
  • +
  • {t("nutrition.instructions.skip_button")}
  • +
  • {t("nutrition.instructions.invalid_image_button")}
diff --git a/src/pages/nutrition/LinksToProduct.tsx b/src/pages/nutrition/LinksToProduct.tsx index 673a9f2c1c..4ba1826e24 100644 --- a/src/pages/nutrition/LinksToProduct.tsx +++ b/src/pages/nutrition/LinksToProduct.tsx @@ -40,7 +40,11 @@ export default function LinkToProduct( > {t("questions.edit")} - Restant: {count} + + {t("questions.remaining", { + count, + })} + ); } diff --git a/src/pages/nutrition/PictureSection.tsx b/src/pages/nutrition/PictureSection.tsx index dee07ec9fc..e2259e94c5 100644 --- a/src/pages/nutrition/PictureSection.tsx +++ b/src/pages/nutrition/PictureSection.tsx @@ -11,6 +11,7 @@ import { TransformWrapper, } from "react-zoom-pan-pinch"; import { OFF_IMAGE_URL } from "../../const"; +import { useTranslation } from "react-i18next"; interface PictureSectionProps { isLoading?: boolean; @@ -20,6 +21,7 @@ interface PictureSectionProps { } export default function PictureSection(props: PictureSectionProps) { + const { t } = useTranslation(); const { isLoading, insight, product, apiRef } = props; const [rotationIndex, setRotationIndex] = React.useState(0); @@ -46,7 +48,7 @@ export default function PictureSection(props: PictureSectionProps) { return (

- Photo upload:{" "} + {`${t("nutrition.pictures.picture_date")}: `} {imageTimestamp ? new Date(imageTimestamp * 1000).toLocaleDateString(undefined, { day: "2-digit", @@ -57,8 +59,8 @@ export default function PictureSection(props: PictureSectionProps) {

{selectedImagesIds.includes(imageId) - ? `Selected as nutriment image ✅` - : `Not selected as nutriment ❌`} + ? t("nutrition.pictures.selected_as_nutrients") + : t("nutrition.pictures.not_selected_as_nutrients")}

setRotationIndex((p) => p - 1)}> diff --git a/src/pages/nutrition/index.tsx b/src/pages/nutrition/index.tsx index d93ece68eb..72f40370cd 100644 --- a/src/pages/nutrition/index.tsx +++ b/src/pages/nutrition/index.tsx @@ -25,8 +25,10 @@ import PictureSection from "./PictureSection"; import Instructions from "./Instructions"; import useNutrimentTranslations from "./useNutrimentTranslations"; import { useCountry } from "../../contexts/CountryProvider"; +import { useTranslation } from "react-i18next"; export default function Nutrition() { + const { t } = useTranslation(); const [partiallyFilled, setPartiallyFilled] = React.useState(false); const [displayOFFValue, setDisplayOFFValue] = React.useState(false); const handlePartiallyFilled = (_, checked) => { @@ -96,7 +98,7 @@ export default function Nutrition() { onChange={handlePartiallyFilled} /> } - label="Tableau partiellement rempli" + label={t("nutrition.table_partialy_filled")} /> } - label="afficher le valeurs OFF" + label={t("nutrition.display_off_values")} /> - Nutriments + {t("nutrition.nutrients")} 100g - serving{" "} + {`${t("nutrition.serving_size")} `}
{notUsedNutriments.map(({ id, name }) => (