From ed60aa76e1045e4d7225afa2bb4f6fd8a9e94aa5 Mon Sep 17 00:00:00 2001 From: VaiTon Date: Sun, 3 Apr 2022 14:35:01 +0200 Subject: [PATCH 1/3] feat: use appbar button actions in nutrition_page_loaded.dart feat: minor design fixes in nutrition_page_loaded.dart --- .../pages/product/nutrition_page_loaded.dart | 83 ++++++++++--------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart index ecf27b0f183..327cc6fbdac 100644 --- a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart +++ b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart @@ -10,6 +10,7 @@ import 'package:provider/provider.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/product_query.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/nutrition_container.dart'; @@ -83,15 +84,21 @@ class _NutritionPageLoadedState extends State { } children.add(_addNutrientButton(appLocalizations)); } - children.add(_addCancelSaveButtons( - appLocalizations, - localDatabase, - )); return Scaffold( - appBar: AppBar(title: Text(appLocalizations.nutrition_page_title)), + appBar: AppBar( + title: Text(appLocalizations.nutrition_page_title), + actions: [ + IconButton( + onPressed: () => _validateAndSave(localDatabase), + icon: const Icon(Icons.check)) + ], + ), body: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.symmetric( + horizontal: LARGE_SPACE, + vertical: SMALL_SPACE, + ), child: Form( key: _formKey, child: ListView(children: children), @@ -216,7 +223,7 @@ class _NutritionPageLoadedState extends State { } Widget _getServingSwitch(final AppLocalizations appLocalizations) => Row( - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text(appLocalizations.nutrition_page_per_100g), @@ -229,11 +236,15 @@ class _NutritionPageLoadedState extends State { ], ); - Widget _switchNoNutrition(final AppLocalizations appLocalizations) => - Container( + Widget _switchNoNutrition(final AppLocalizations localizations) => SmoothCard( color: Theme.of(context).colorScheme.primary, + padding: const EdgeInsets.symmetric( + horizontal: MEDIUM_SPACE, + vertical: SMALL_SPACE, + ), + margin: EdgeInsets.zero, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ Switch( @@ -241,14 +252,11 @@ class _NutritionPageLoadedState extends State { onChanged: (final bool value) => setState(() => _unspecified = !_unspecified), ), - SizedBox( - width: getColumnSizeFromContext(context, 0.6), - child: Text( - appLocalizations.nutrition_page_unspecified, - style: const TextStyle(color: Colors.white), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), + Text( + localizations.nutrition_page_unspecified, + style: Theme.of(context).primaryTextTheme.bodyText1, + maxLines: 2, + overflow: TextOverflow.ellipsis, ), ], ), @@ -260,8 +268,10 @@ class _NutritionPageLoadedState extends State { final List leftovers = List.from( _nutritionContainer.getLeftoverNutrients(), ); + leftovers.sort((final OrderedNutrient a, final OrderedNutrient b) => a.name!.compareTo(b.name!)); + final OrderedNutrient? selected = await showDialog( context: context, builder: (BuildContext context) { @@ -294,33 +304,24 @@ class _NutritionPageLoadedState extends State { setState(() => _nutritionContainer.add(selected)); } }, + style: ButtonStyle( + shape: MaterialStateProperty.all( + const RoundedRectangleBorder( + borderRadius: ROUNDED_BORDER_RADIUS, + side: BorderSide.none, + ), + ), + ), icon: const Icon(Icons.add), label: Text(appLocalizations.nutrition_page_add_nutrient), ); - Widget _addCancelSaveButtons( - final AppLocalizations appLocalizations, - final LocalDatabase localDatabase, - ) => - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => Navigator.pop(context), - child: Text(appLocalizations.cancel), - ), - ElevatedButton( - onPressed: () async { - if (!_formKey.currentState!.validate()) { - return; - } - await _save(localDatabase); - }, - child: Text(appLocalizations.save), - ), - ], - ); + Future _validateAndSave(final LocalDatabase localDatabase) async { + if (!_formKey.currentState!.validate()) { + return; + } + await _save(localDatabase); + } Future _save(final LocalDatabase localDatabase) async { for (final String key in _controllers.keys) { From 6830058a4a13baaa99ff4c9dcaef13f674079926 Mon Sep 17 00:00:00 2001 From: VaiTon Date: Sun, 3 Apr 2022 21:57:28 +0200 Subject: [PATCH 2/3] feat: add dialog to confirm save or back press in nutrition_page_loaded.dart --- packages/smooth_app/lib/l10n/app_en.arb | 1248 +++++++++-------- .../pages/product/nutrition_page_loaded.dart | 98 +- 2 files changed, 699 insertions(+), 647 deletions(-) diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 6eaacf56345..39251faf4f6 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -1,626 +1,628 @@ { - "@Utils": {}, - "yes": "Yes", - "@yes": {}, - "add": "Add", - "@add": {}, - "okay": "Okay", - "@okay": {}, - "applyButtonText": "Apply", - "@applyButtonText": {}, - "next_label": "Next", - "@next_label": { - "description": "A label on a button that says 'Next', pressing the button takes the user to the next screen." - }, - "save": "Save", - "skip": "Skip", - "cancel": "Cancel", - "@cancel": {}, - "close": "Close", - "@close": {}, - "no": "No", - "@no": {}, - "stop": "Stop", - "@stop": {}, - "finish": "Finish", - "@finish": {}, - "reset_food_prefs": "Reset food preferences", - "@reset": { - "description": "Button label, clicking on the button will reset user's food preferences." - }, - "error": "Something went wrong", - "@error": {}, - "error_occurred": "An error occurred", - "@error_occurred": {}, - "featureInProgress": "We're still working on this feature, stay tuned", - "@featureInProgress": {}, - "label_web": "View on the Web", - "@label_web": {}, - "learnMore": "Learn more", - "@learnMore": {}, - "incompatible": "Incompatible", - "@incompatible": { - "description": "Short label for product list view: the product is incompatible with your preferences" - }, - "compatible": "Compatible", - "@compatible": { - "description": "Short label for product list view: the product is compatible with your preferences" - }, - "unknown": "Unknown", - "@unknown": { - "description": "Short label for product list view: the compatibility of that product with your preferences is unknown" - }, - "licenses": "Licences", - "@licenses": {}, - "looking_for": "Looking for", - "@looking_for": { - "description": "Looking for: ${BARCODE}" - }, - "@Introduction screen": {}, - "welcomeToOpenFoodFacts": "Welcome to Open\u00A0Food\u00A0Facts", - "@welcomeToOpenFoodFacts": {}, - "whatIsOff": "Open Food Facts is a global non-profit powered by local communities.", - "@whatIsOff": { - "description": "Description of Open Food Facts organization." - }, - "offUtility": "Choose foods that are good for you and the planet.", - "@offUtility": { - "description": "Description of what a user can use Open Food Facts for." - }, - "productDataUtility": "See the food data relevant to your preferences.", - "@productDataUtility": { - "description": "Description of what a user can use the product data for." - }, - "healthCardUtility": "Choose foods that are good for you.", - "@healthCardUtility": { - "description": "Description of what a user can use the health data in a product for." - }, - "ecoCardUtility": "Choose foods that are good for the planet.", - "@ecoCardUtility": { - "description": "Description of what a user can use the Eco data in a product for." - }, - "@user_management": {}, - "sign_in_text": "Sign in to your Open Food Facts account to save your contributions", - "incorrect_credentials": "Incorrect username or password.", - "login": "Login", - "@login": { - "description": "Text field hint: unified name for either username or e-mail address" - }, - "login_page_username_or_email": "Please enter username or e-mail", - "login_page_password_error_empty": "Please enter a password", - "create_account": "Create account", - "@create_account": { - "description": "Button label: Opens a page where a new user can register" - }, - "sign_in": "Sign in", - "@sign_in": { - "description": "Button label: For sign in" - }, - "sign_out": "Sign out", - "@sign_out": { - "description": "Button label: For sign out" - }, - "sign_out_confirmation": "Are you sure you want to sign out?", - "@sign_out_confirmation": { - "description": "Pop up title: Reassuring if the user really want to sign out" - }, - "password": "Password", - "forgot_password": "Forgot password", - "@forgot_password": { - "description": "Button label: Opens a page where a password reset e-mail can be requested" - }, - "view_profile": "View profile", - "@view_profile": { - "description": "Button label: For to show your account" - }, - "reset_password": "Reset password", - "@reset_password": { - "description": "Forgot password page title" - }, - "reset_password_explanation_text": "In case of a forgotten password, enter your username or e-mail address to receive instructions for a password reset. Also, remember to check the Spam folder.", - "username_or_email": "Username or e-mail", - "@username_or_email": { - "description": "Text field hint for password reset" - }, - "reset_password_done": "An e-mail with a link to reset your password has been sent to the e-mail address associated with your account. Also check your spam", - "send_reset_password_mail": "Change password", - "@send_reset_password_mail": { - "description": "Button label: Submit the password reset e-mail request" - }, - "enter_some_text": "Please enter some text", - "@enter_some_text": { - "description": "Error when a required text field is empty" - }, - "sign_up_page_title": "Sign Up", - "@sign_up_page_title": { - "description": "Header" - }, - "sign_up_page_action_button": "Sign Up", - "@sign_up_page_action_button": { - "description": "Button for signing up" - }, - "sign_up_page_action_doing_it": "Signing up...", - "@sign_up_page_action_doing_it": { - "description": "Progress indicator dialog during the actual signing up process" - }, - "sign_up_page_action_ok": "Congratulations! Your account has just been created.", - "sign_up_page_display_name_hint": "Name", - "sign_up_page_display_name_error_empty": "Please enter the display name you want to use", - "sign_up_page_email_hint": "E-mail", - "sign_up_page_email_error_empty": "E-mail is required", - "sign_up_page_email_error_invalid": "This e-mail is invalid", - "sign_up_page_username_hint": "Username", - "sign_up_page_username_error_empty": "Please enter a username", - "sign_up_page_username_error_invalid": "Please enter a valid username", - "sign_up_page_username_description": "Username cannot contains spaces, caps or special characters", - "sign_up_page_password_hint": "Password", - "sign_up_page_password_error_empty": "Please enter a password", - "sign_up_page_password_error_invalid": "Please enter a valid password (at least 6 characters)", - "sign_up_page_confirm_password_hint": "Confirm Password", - "sign_up_page_confirm_password_error_empty": "Please confirm the password", - "sign_up_page_confirm_password_error_invalid": "Passwords don't match", - "sign_up_page_agree_url": "https://world-en.openfoodfacts.org/terms-of-use", - "@sign_up_page_agree_url": { - "description": "Please insert the right url here. Go to the openfoodfacts homepage, switch to your country and then on the bottom left footer is Terms of use from which the url should be taken" - }, - "donate_url": "https://donate.openfoodfacts.org/", - "@donate_url": { - "description": "Please insert the right url from the website here." - }, - "sign_up_page_agree_error_invalid": "When creating an account, agreeing to the Terms of Use is mandatory, however, anonymous contributions can still be made through the app", - "@sign_up_page_agree_error_invalid": { - "description": "Error message: You have to agree to the terms-of-use (A checkbox to do so is above this error message)" - }, - "sign_up_page_producer_checkbox": "I am a food producer", - "sign_up_page_producer_hint": "Producer/brand", - "sign_up_page_producer_error_empty": "Please enter a producer or a brand name", - "sign_up_page_subscribe_checkbox": "I'd like to subscribe to the Open Food Facts newsletter (You can unsubscribe from it at any time)", - "@Settings": {}, - "settingsTitle": "Settings", - "@settingsTitle": { - "description": "The title of the Settings page" - }, - "darkmode": "Darkmode", - "@darkmode": { - "description": "The name of the darkmode on off switch" - }, - "darkmode_dark": "Dark", - "@darkmode_dark": { - "description": "Indicator inside the darkmode switch" - }, - "darkmode_light": "Light", - "@darkmode_light": { - "description": "Indicator inside the darkmode switch" - }, - "thanks_for_contributing": "Thanks for contributing", - "@contributors": { - "description": "Button label: Opens a pop up window where all contributors of this app are shown" - }, - "contributors": "Contributors", - "support": "Support", - "@support": { - "description": "Button label: Opens a pop up window where all ways to get support are shown" - }, - "support_join_slack": "Ask for help in our Slack channel", - "support_via_email": "Send us an e-mail", - "termsOfUse": "Terms of use", - "@termsOfUse": {}, - "about_this_app": "About this app", - "@about_this_app": { - "description": "Button label: Opens a pop up window which shows information about the app" - }, - "@About this app section": {}, - "contribute": "Contribute", - "@contribute": { - "description": "Button label: Shows multiple ways how users can contribute to OFF" - }, - "contribute_sw_development": "Software development", - "@contribute_sw_development": { - "description": "Button label + page title: Ways to help" - }, - "contribute_develop_text": "The code for every Open Food Facts product is available on GitHub. You are welcome to reuse the code (it's open source) and help us improve it, for everyone, on all the planet.", - "@contribute_develop_text": {}, - "contribute_develop_text_2": "You can join the Open Food Facts Slack chatroom which is the preferred way to ask questions.", - "@contribute_develop_text_2": {}, - "contribute_donate_header": "Donate to Open Food Facts", - "@contribute_donate_header": {}, - "contribute_improve_ProductsToBeCompleted": "Products to be completed", - "@contribute_improve_ProductsToBeCompleted": { - "description": "Button label: Shows a list of products which aren't completed" - }, - "contribute_improve_header": "Improving", - "@contribute_improve_header": { - "description": "Button label + page title: Ways to improve the database" - }, - "contribute_improve_text": "The database is the core of the project. It's easy and very quick to help. You can download the mobile app for your phone, and start adding or improving products.\n\nOn the other hand, Open Food Facts website offers many ways to contribute: ", - "@contribute_improve_text": {}, - "contribute_translate_header": "Translate", - "@contribute_translate_header": { - "description": "Button label + pop up window title: Shows information about helping by translating" - }, - "contribute_translate_link_text": "Start Translating", - "@contribute_translate_link_text": { - "description": "Button label: Opens the Crowdin translation portal" - }, - "contribute_translate_text": "Open Food Facts is a global project, containing products from more than 160 countries. Open Food Facts is translated into dozens of languages, with constantly evolving content.", - "@contribute_translate_text": {}, - "contribute_translate_text_2": "Translations is one of the key tasks of the project", - "@contribute_translate_text_2": {}, - "tap_to_answer": "Tap here to answer questions", - "@tap_to_answer": { - "description": "Button label shown on a product, clicking the button opens a card with unanswered product questions, users can answer these to contribute to Open food facts and gain rewards." - }, - "saving_answer": "Saving your answer", - "@saving_answer": { - "description": "Dialog shown to users after they have answered a question, while the answer is being saved in the BE." - }, - "contribute_to_get_rewards": "Help improve food transparency and get rewards", - "@contribute_to_get_rewards": { - "description": "Button description shown on a product, clicking the button opens a card with unanswered product questions, users can answer these to contribute to Open food facts and gain rewards." - }, - "@Personal preferences": {}, - "myPreferences": "My preferences", - "@myPreferences": { - "description": "Page title: Page where the ranking preferences can be changed" - }, - "myPreferences_profile_title": "Your Profile", - "myPreferences_profile_subtitle": "Change app settings and get advice.", - "myPreferences_settings_title": "App Settings", - "myPreferences_settings_subtitle": "Dark mode, Theme, ...", - "myPreferences_food_title": "Food Preferences", - "myPreferences_food_subtitle": "Choose what information about food matters most to you.", - "confirmResetPreferences": "Reset your food preferences?", - "@confirmResetPreferences": { - "description": "Pop up title: Reassuring if the food preferences should really be reset" - }, - "myPersonalizedRanking": "My personalized ranking", - "@myPersonalizedRanking": { - "description": "When you press this button, all products (in list or category) are sorted according to your preferences." - }, - "ranking_tab_all": "All", - "ranking_subtitle_match_yes": "A great match for you", - "ranking_subtitle_match_no": "Very poor match", - "ranking_subtitle_match_maybe": "Unknown match", - "reloaded_with_new_preferences": "Reloaded with new preferences", - "@reloaded_with_new_preferences": { - "description": "Snackbar title: Shows that the modified settings have been applied" - }, - "@other": {}, - "profile_navbar_label": "Profile", - "@profile_navbar_label": { - "description": "BottomNavigationBarLabel: For the profile and personal preferences page" - }, - "scan_navbar_label": "Search", - "@scan_navbar_label": { - "description": "BottomNavigationBarLabel: For the searching of products" - }, - "history_navbar_label": "History", - "@history_navbar_label": { - "description": "BottomNavigationBarLabel: For the history and compare mode" - }, - "category": "Filter by category", - "@category": { - "description": "From a product list, there's a category filter: this is its title" - }, - "category_all": "All", - "@category_al": { - "description": "Top meta-entry on a category filter" - }, - "category_search": "(category search)", - "filter": "Filter", - "@filter": { - "description": "A button that opens a menu where you can filter within categories. Juices => Apple juices/Orange juices" - }, - "scan": "Scan", - "@scan": { - "description": "Page title: List type: Scanned products" - }, - "search": "Search", - "@search": { - "description": "Hint text of a search text input field" - }, - "tab_for_more": "Tap to see more info...", - "@Product": {}, - "product": "Product", - "@product": {}, - "unknownBrand": "Unknown brand", - "@unknownBrand": {}, - "unknownProductName": "Unknown product name", - "@unknownProductName": {}, - "label_refresh": "Refresh", - "@label_refresh": { - "description": "Refresh the cached product" - }, - "image": "Image", - "front_photo": "Front photo", - "@front_photo": { - "description": "Button label: For adding a picture of the front of a product" - }, - "ingredients": "Ingredients", - "@ingredients": {}, - "ingredients_editing_instructions": "Keep the original order. Indicate the percentage when specified. Separate with a comma or hyphen, use parentheses for ingredients of an ingredient, and indicate allergens between underscores.", - "ingredients_editing_error": "Failed to save the ingredients.", - "ingredients_editing_image_error": "Failed to get a new ingredients image.", - "ingredients_editing_title": "Edit Ingredients", - "ingredients_photo": "Ingredients photo", - "@ingredients_photo": { - "description": "Button label: For adding a picture of the Ingredients of a product" - }, - "nutrition": "Nutrition", - "@nutrition": {}, - "nutrition_facts_photo": "Nutrition facts photo", - "@nutrition_facts_photo": { - "description": "Button label: For adding a picture of the nutrition facts of a product" - }, - "packaging_information": "Packaging information", - "@packaging_information": { - "description": "Button label: For adding a picture of the packaging of a product" - }, - "packaging_information_photo": "Packaging information photo", - "@packaging_information_photo": {}, - "missing_product": "You found a new product!", - "@missing_product": {}, - "add_product_take_photos": "Take photos of the packaging to add this product to Open Food Facts", - "@add_product_take_photos": {}, - "add_product_take_photos_descriptive": "Please take some photos and the Open Food Facts engine can work out the rest!", - "@add_product_take_photos_descriptive": {}, - "add_product_information_button_label": "Add product information", - "@add_product_information_button_label": {}, - "new_product": "New Product", - "@new_product": {}, - "front_packaging_photo_button_label": "Front packaging photo", - "@front_packaging_photo_button_label": {}, - "confirm_front_packaging_photo_button_label": "Confirm Front packaging photo", - "@confirm_front_packaging_photo_button_label": { - "description": "Button clicking on which confirms the picture of the front packaging that user just took." - }, - "front_photo_uploaded": "Front photo uploaded", - "@front_photo_uploaded": {}, - "ingredients_photo_button_label": "Ingredients photo", - "@ingredients_photo_button_label": {}, - "confirm_ingredients_photo_button_label": "Confirm Ingredients photo", - "@confirm_ingredients_photo_button_label": { - "description": "Button clicking on which confirms the picture of ingredients that user just took." - }, - "ingredients_photo_uploaded": "Ingredients photo uploaded", - "@ingredients_photo_uploaded": {}, - "nutritional_facts_photo_button_label": "Nutritional facts photo", - "@nutritional_facts_photo_button_label": {}, - "confirm_nutritional_facts_photo_button_label": "Confirm Nutritional facts photo", - "@confirm_nutritional_facts_photo_button_label": { - "description": "Button clicking on which confirms the picture of nutritional facts that user just took." - }, - "nutritional_facts_photo_uploaded": "Nutritional facts photo uploaded", - "@nutritional_facts_photo_uploaded": {}, - "recycling_photo_button_label": "Recycling information photo", - "@recycling_photo_button_label": {}, - "confirm_recycling_photo_button_label": "Confirm Recycling information photo", - "@confirm_recycling_photo_button_label": { - "description": "Button clicking on which confirms the picture of recycling information that user just took." - }, - "recycling_photo_uploaded": "Recycling photo uploaded", - "@recycling_photo_uploaded": {}, - "other_interesting_photo_button_label": "Other interesting photos", - "@other_interesting_photo_button_label": {}, - "confirm_other_interesting_photo_button_label": "Confirm photo", - "@confirm_other_interesting_photo_button_label": { - "description": "Button clicking on which confirms a miscellaneous photo of the product." - }, - "other_photo_uploaded": "Miscellaneous photo uploaded", - "@other_photo_uploaded": {}, - "retake_photo_button_label": "Retake", - "@retake_photo_button_label": { - "description": "Button clicking on which allows users to retake the last photo they took." - }, - "selecting_photo": "Selecting photo", - "@selecting_photo": { - "description": "Progress indicator when the users takes a photo" - }, - "uploading_image": "Uploading Image to the server", - "@uploading_image": { - "description": "Message when a new picture is uploading to the server" - }, - "score_add_missing_ingredients": "Add missing ingredients", - "score_add_missing_nutrition_facts": "Add missing nutrition facts", - "score_add_missing_product_category": "Add missing product category", - "score_update_nutrition_facts": "Update nutrition facts", - "nutrition_page_title": "Product Nutrition Facts", - "nutrition_page_unspecified": "Nutrition facts are not specified on the product", - "nutrition_page_per_100g": "per 100g", - "nutrition_page_per_serving": "per serving", - "nutrition_page_add_nutrient": "Add a nutrient", - "nutrition_page_serving_size": "Serving size", - "nutrition_page_invalid_number": "Invalid number", - "nutrition_page_update_running": "Updating the product on the server...", - "nutrition_page_update_done": "Product updated!", - "more_photos": "More interesting photos", - "@more_photos": {}, - "no_product_found": "No product found", - "@no_product_found": {}, - "not_found": "not found:", - "searchPanelHeader": "Search or scan your first product", - "@Product query status": {}, - "refreshing_product": "Refreshing product", - "@refreshing_product": { - "description": "Confirmation, that the product data of a cached product is queried again" - }, - "product_refreshed": "Product refreshed", - "@product_refreshed": { - "description": "Confirmation, that the product data refresh is done" - }, - "could_not_refresh": "Could not refresh product", - "@could_not_refresh": { - "description": "The product data couldn't be refreshed" - }, - "product_internet_cancel": "Canceled by user.", - "@product_internet_cancel": { - "description": "Confirmation, that the product data got canceled by the user" - }, - "product_internet_error": "Impossible to fetch information about this product due to a network error.", - "cached_results_from": "Show results from:", - "@cached_results_from": { - "description": "Cached results from: x time ago (time ago should not be added to the string)" - }, - "@Product Addition": {}, - "added_product_thanks": "Thank you for adding this product!", - "@added_product_thanks": {}, - "product_search_same_category": "Compare to Category", - "@product_search_same_category": { - "description": "Button looking for the other products within the same category. Less than 30 characters" - }, - "product_improvement_add_category": "Add a category to calculate the Nutri-Score.", - "@product_improvement_add_category": { - "description": "Message for ProductImprovement.ADD_CATEGORY" - }, - "product_improvement_add_nutrition_facts": "Add nutrition facts to calculate the Nutri-Score.", - "@product_improvement_add_nutrition_facts": { - "description": "Message for ProductImprovement.ADD_NUTRITION_FACTS" - }, - "product_improvement_add_nutrition_facts_and_category": "Add nutrition facts and a category to calculate the Nutri-Score.", - "@product_improvement_add_nutrition_facts_and_category": { - "description": "Message for ProductImprovement.ADD_NUTRITION_FACTS_AND_CATEGORY" - }, - "product_improvement_categories_but_no_nutriscore": "The Nutri-Score for this product can't be calculated, which may be due to e.g. a non-standard category. If this is considered an error, please contact us.", - "@product_improvement_categories_but_no_nutriscore": { - "description": "Message for ProductImprovement.CATEGORIES_BUT_NO_NUTRISCORE" - }, - "product_improvement_obsolete_nutrition_image": "The nutrition image is obsolete: please refresh it.", - "@product_improvement_obsolete_nutrition_image": { - "description": "Message for ProductImprovement.OBSOLETE_NUTRITION_IMAGE" - }, - "product_improvement_origins_to_be_completed": "The Eco-Score takes into account the origins of the ingredients. Please take a photo of the ingredient list and/or any geographic claim or edit the product, so they can be taken into account.", - "@product_improvement_origins_to_be_completed": { - "description": "Message for ProductImprovement.ORIGINS_TO_BE_COMPLETED" - }, - "country_chooser_label": "Please choose a country", - "@country_chooser_label": { - "description": "Label shown above a selector where the user can select their country" - }, - "country_selection_explanation": "Some environmental features are location-specific", - "@country_label": { - "description": "Explanation as to why users should select their country." - }, - "product_removed_comparison": "Product removed from comparison", - "@product_removed_comparison": { - "description": "Product got removed from comparison list" - }, - "product_removed_history": "Product removed from history", - "@product_removed_history": { - "description": "Product got removed from history" - }, - "product_could_not_remove": "Could not remove product", - "@product_could_not_remove": { - "description": "Could not remove product from a list" - }, - "@Lists": {}, - "no_prodcut_in_list": "There is no product in this list", - "no_product_in_section": "There is no product in this section", - "recently_seen_products": "History", - "clear": "Clear", - "@clear": { - "description": "Clears a product list" - }, - "really_clear": "Do you really want to delete this list?", - "product_compatibility_unknown": "Compatibility Unknown", - "@product_compatibility_unknown": { - "description": "Product compatibility summary title" - }, - "product_compatibility_incompatible": "Doesn't match", - "@product_compatibility_incompatible": { - "description": "Product compatibility summary title" - }, - "product_compatibility_good": "Good Match", - "@product_compatibility_good": { - "description": "Product compatibility summary title" - }, - "@Plural": {}, - "pct_match": "{percent}% match", - "@pct_match": { - "description": "This product has a x percent match with your preferences", - "placeholders": { - "percent": {} - } - }, - "plural_ago_days": "{count,plural, one {} =1{one day ago} other{{count} days ago}}", - "@plural_ago_days": { - "description": "Cached results from: x days ago", - "placeholders": { - "count": {} - } - }, - "plural_ago_hours": "{count,plural, one {} =1{one hour ago} other{{count} hours ago}}", - "@plural_ago_hours": { - "description": "Cached results from: x hours ago", - "placeholders": { - "count": {} - } - }, - "plural_ago_minutes": "{count,plural, one {} =0{less than a minute ago} =1{one minute ago} other{{count} minutes ago}}", - "@plural_ago_minutes": { - "description": "Cached results from: x minutes ago", - "placeholders": { - "count": {} - } - }, - "plural_ago_months": "{count,plural, one {} =1{one month ago} other{{count} months ago}}", - "@plural_ago_months": { - "description": "Cached results from: x months ago", - "placeholders": { - "count": {} - } - }, - "plural_ago_weeks": "{count,plural, one {} =1{one week ago} other{{count} weeks ago}}", - "@plural_ago_weeks": { - "description": "Cached results from: x weeks ago", - "placeholders": { - "count": {} - } - }, - "plural_compare_x_products": "{count,plural, other{Compare {count} Products}", - "@plural_compare_x_products": { - "description": "Button label", - "placeholders": { - "count": {} - } - }, - "compare_products_mode": "Compare", - "@compare_products_mode": { - "description": "Button to switch to 'compare products mode'" - }, - "retry_button_label": "Retry", - "connect_with_us": "Connect with us", - "instagram": "Instagram", - "twitter": "Twitter", - "blog": "Blog", - "faq": "FAQ", - "discover": "Discover", - "how_to_contribute": "How to Contribute", - "main_app_color": "Theme", - "@main_app_color": { - "description": "Heading for the section to pick the main app color" - }, - "hint_knowledge_panel_message": "Your can tap on any part of the card to get more details about what you see. Try it now!", - "@hint_knowledge_panel_message": { - "description": "Hint popup indicating the card is clickable during onboarding" - }, - "consent_analytics_title": "Send anonymous analytics", - "@consent_analytics_title": { - "description": "Title for the consent analytics UI Page" - }, - "consent_analytics_body1": "Help the Open Food Facts volunteer to improve the app. You decide if you want to send anonymous analytics.", - "@conset_analytics_body1": { - "description": "first paragraph for the consent analytics UI Page" - }, - "consent_analytics_body2": "If you change your mind this option can be enabled and disabled at any time from the settings.", - "@consent_analytics_body2": { - "description": "second paragraph for the consent analytics UI Page" - }, - "authorize_button_label": "Authorize", - "@authorize": { - "description": "Button to accept the request of sending the anonymous analytics" - }, - "refuse_button_label": "Refuse", - "@refuse": { - "description": "Button to decline the request of sending the anonymous analytics" + "@Utils": {}, + "yes": "Yes", + "@yes": {}, + "add": "Add", + "@add": {}, + "okay": "Okay", + "@okay": {}, + "applyButtonText": "Apply", + "@applyButtonText": {}, + "next_label": "Next", + "@next_label": { + "description": "A label on a button that says 'Next', pressing the button takes the user to the next screen." + }, + "save": "Save", + "save_confirmation": "Are you sure you want to save?", + "skip": "Skip", + "cancel": "Cancel", + "@cancel": {}, + "close": "Close", + "@close": {}, + "no": "No", + "@no": {}, + "stop": "Stop", + "@stop": {}, + "finish": "Finish", + "@finish": {}, + "reset_food_prefs": "Reset food preferences", + "@reset": { + "description": "Button label, clicking on the button will reset user's food preferences." + }, + "error": "Something went wrong", + "@error": {}, + "error_occurred": "An error occurred", + "@error_occurred": {}, + "featureInProgress": "We're still working on this feature, stay tuned", + "@featureInProgress": {}, + "label_web": "View on the Web", + "@label_web": {}, + "learnMore": "Learn more", + "@learnMore": {}, + "incompatible": "Incompatible", + "@incompatible": { + "description": "Short label for product list view: the product is incompatible with your preferences" + }, + "compatible": "Compatible", + "@compatible": { + "description": "Short label for product list view: the product is compatible with your preferences" + }, + "unknown": "Unknown", + "@unknown": { + "description": "Short label for product list view: the compatibility of that product with your preferences is unknown" + }, + "licenses": "Licences", + "@licenses": {}, + "looking_for": "Looking for", + "@looking_for": { + "description": "Looking for: ${BARCODE}" + }, + "@Introduction screen": {}, + "welcomeToOpenFoodFacts": "Welcome to Open\u00A0Food\u00A0Facts", + "@welcomeToOpenFoodFacts": {}, + "whatIsOff": "Open Food Facts is a global non-profit powered by local communities.", + "@whatIsOff": { + "description": "Description of Open Food Facts organization." + }, + "offUtility": "Choose foods that are good for you and the planet.", + "@offUtility": { + "description": "Description of what a user can use Open Food Facts for." + }, + "productDataUtility": "See the food data relevant to your preferences.", + "@productDataUtility": { + "description": "Description of what a user can use the product data for." + }, + "healthCardUtility": "Choose foods that are good for you.", + "@healthCardUtility": { + "description": "Description of what a user can use the health data in a product for." + }, + "ecoCardUtility": "Choose foods that are good for the planet.", + "@ecoCardUtility": { + "description": "Description of what a user can use the Eco data in a product for." + }, + "@user_management": {}, + "sign_in_text": "Sign in to your Open Food Facts account to save your contributions", + "incorrect_credentials": "Incorrect username or password.", + "login": "Login", + "@login": { + "description": "Text field hint: unified name for either username or e-mail address" + }, + "login_page_username_or_email": "Please enter username or e-mail", + "login_page_password_error_empty": "Please enter a password", + "create_account": "Create account", + "@create_account": { + "description": "Button label: Opens a page where a new user can register" + }, + "sign_in": "Sign in", + "@sign_in": { + "description": "Button label: For sign in" + }, + "sign_out": "Sign out", + "@sign_out": { + "description": "Button label: For sign out" + }, + "sign_out_confirmation": "Are you sure you want to sign out?", + "@sign_out_confirmation": { + "description": "Pop up title: Reassuring if the user really want to sign out" + }, + "password": "Password", + "forgot_password": "Forgot password", + "@forgot_password": { + "description": "Button label: Opens a page where a password reset e-mail can be requested" + }, + "view_profile": "View profile", + "@view_profile": { + "description": "Button label: For to show your account" + }, + "reset_password": "Reset password", + "@reset_password": { + "description": "Forgot password page title" + }, + "reset_password_explanation_text": "In case of a forgotten password, enter your username or e-mail address to receive instructions for a password reset. Also, remember to check the Spam folder.", + "username_or_email": "Username or e-mail", + "@username_or_email": { + "description": "Text field hint for password reset" + }, + "reset_password_done": "An e-mail with a link to reset your password has been sent to the e-mail address associated with your account. Also check your spam", + "send_reset_password_mail": "Change password", + "@send_reset_password_mail": { + "description": "Button label: Submit the password reset e-mail request" + }, + "enter_some_text": "Please enter some text", + "@enter_some_text": { + "description": "Error when a required text field is empty" + }, + "sign_up_page_title": "Sign Up", + "@sign_up_page_title": { + "description": "Header" + }, + "sign_up_page_action_button": "Sign Up", + "@sign_up_page_action_button": { + "description": "Button for signing up" + }, + "sign_up_page_action_doing_it": "Signing up...", + "@sign_up_page_action_doing_it": { + "description": "Progress indicator dialog during the actual signing up process" + }, + "sign_up_page_action_ok": "Congratulations! Your account has just been created.", + "sign_up_page_display_name_hint": "Name", + "sign_up_page_display_name_error_empty": "Please enter the display name you want to use", + "sign_up_page_email_hint": "E-mail", + "sign_up_page_email_error_empty": "E-mail is required", + "sign_up_page_email_error_invalid": "This e-mail is invalid", + "sign_up_page_username_hint": "Username", + "sign_up_page_username_error_empty": "Please enter a username", + "sign_up_page_username_error_invalid": "Please enter a valid username", + "sign_up_page_username_description": "Username cannot contains spaces, caps or special characters", + "sign_up_page_password_hint": "Password", + "sign_up_page_password_error_empty": "Please enter a password", + "sign_up_page_password_error_invalid": "Please enter a valid password (at least 6 characters)", + "sign_up_page_confirm_password_hint": "Confirm Password", + "sign_up_page_confirm_password_error_empty": "Please confirm the password", + "sign_up_page_confirm_password_error_invalid": "Passwords don't match", + "sign_up_page_agree_url": "https://world-en.openfoodfacts.org/terms-of-use", + "@sign_up_page_agree_url": { + "description": "Please insert the right url here. Go to the openfoodfacts homepage, switch to your country and then on the bottom left footer is Terms of use from which the url should be taken" + }, + "donate_url": "https://donate.openfoodfacts.org/", + "@donate_url": { + "description": "Please insert the right url from the website here." + }, + "sign_up_page_agree_error_invalid": "When creating an account, agreeing to the Terms of Use is mandatory, however, anonymous contributions can still be made through the app", + "@sign_up_page_agree_error_invalid": { + "description": "Error message: You have to agree to the terms-of-use (A checkbox to do so is above this error message)" + }, + "sign_up_page_producer_checkbox": "I am a food producer", + "sign_up_page_producer_hint": "Producer/brand", + "sign_up_page_producer_error_empty": "Please enter a producer or a brand name", + "sign_up_page_subscribe_checkbox": "I'd like to subscribe to the Open Food Facts newsletter (You can unsubscribe from it at any time)", + "@Settings": {}, + "settingsTitle": "Settings", + "@settingsTitle": { + "description": "The title of the Settings page" + }, + "darkmode": "Darkmode", + "@darkmode": { + "description": "The name of the darkmode on off switch" + }, + "darkmode_dark": "Dark", + "@darkmode_dark": { + "description": "Indicator inside the darkmode switch" + }, + "darkmode_light": "Light", + "@darkmode_light": { + "description": "Indicator inside the darkmode switch" + }, + "thanks_for_contributing": "Thanks for contributing", + "@contributors": { + "description": "Button label: Opens a pop up window where all contributors of this app are shown" + }, + "contributors": "Contributors", + "support": "Support", + "@support": { + "description": "Button label: Opens a pop up window where all ways to get support are shown" + }, + "support_join_slack": "Ask for help in our Slack channel", + "support_via_email": "Send us an e-mail", + "termsOfUse": "Terms of use", + "@termsOfUse": {}, + "about_this_app": "About this app", + "@about_this_app": { + "description": "Button label: Opens a pop up window which shows information about the app" + }, + "@About this app section": {}, + "contribute": "Contribute", + "@contribute": { + "description": "Button label: Shows multiple ways how users can contribute to OFF" + }, + "contribute_sw_development": "Software development", + "@contribute_sw_development": { + "description": "Button label + page title: Ways to help" + }, + "contribute_develop_text": "The code for every Open Food Facts product is available on GitHub. You are welcome to reuse the code (it's open source) and help us improve it, for everyone, on all the planet.", + "@contribute_develop_text": {}, + "contribute_develop_text_2": "You can join the Open Food Facts Slack chatroom which is the preferred way to ask questions.", + "@contribute_develop_text_2": {}, + "contribute_donate_header": "Donate to Open Food Facts", + "@contribute_donate_header": {}, + "contribute_improve_ProductsToBeCompleted": "Products to be completed", + "@contribute_improve_ProductsToBeCompleted": { + "description": "Button label: Shows a list of products which aren't completed" + }, + "contribute_improve_header": "Improving", + "@contribute_improve_header": { + "description": "Button label + page title: Ways to improve the database" + }, + "contribute_improve_text": "The database is the core of the project. It's easy and very quick to help. You can download the mobile app for your phone, and start adding or improving products.\n\nOn the other hand, Open Food Facts website offers many ways to contribute: ", + "@contribute_improve_text": {}, + "contribute_translate_header": "Translate", + "@contribute_translate_header": { + "description": "Button label + pop up window title: Shows information about helping by translating" + }, + "contribute_translate_link_text": "Start Translating", + "@contribute_translate_link_text": { + "description": "Button label: Opens the Crowdin translation portal" + }, + "contribute_translate_text": "Open Food Facts is a global project, containing products from more than 160 countries. Open Food Facts is translated into dozens of languages, with constantly evolving content.", + "@contribute_translate_text": {}, + "contribute_translate_text_2": "Translations is one of the key tasks of the project", + "@contribute_translate_text_2": {}, + "tap_to_answer": "Tap here to answer questions", + "@tap_to_answer": { + "description": "Button label shown on a product, clicking the button opens a card with unanswered product questions, users can answer these to contribute to Open food facts and gain rewards." + }, + "saving_answer": "Saving your answer", + "@saving_answer": { + "description": "Dialog shown to users after they have answered a question, while the answer is being saved in the BE." + }, + "contribute_to_get_rewards": "Help improve food transparency and get rewards", + "@contribute_to_get_rewards": { + "description": "Button description shown on a product, clicking the button opens a card with unanswered product questions, users can answer these to contribute to Open food facts and gain rewards." + }, + "@Personal preferences": {}, + "myPreferences": "My preferences", + "@myPreferences": { + "description": "Page title: Page where the ranking preferences can be changed" + }, + "myPreferences_profile_title": "Your Profile", + "myPreferences_profile_subtitle": "Change app settings and get advice.", + "myPreferences_settings_title": "App Settings", + "myPreferences_settings_subtitle": "Dark mode, Theme, ...", + "myPreferences_food_title": "Food Preferences", + "myPreferences_food_subtitle": "Choose what information about food matters most to you.", + "confirmResetPreferences": "Reset your food preferences?", + "@confirmResetPreferences": { + "description": "Pop up title: Reassuring if the food preferences should really be reset" + }, + "myPersonalizedRanking": "My personalized ranking", + "@myPersonalizedRanking": { + "description": "When you press this button, all products (in list or category) are sorted according to your preferences." + }, + "ranking_tab_all": "All", + "ranking_subtitle_match_yes": "A great match for you", + "ranking_subtitle_match_no": "Very poor match", + "ranking_subtitle_match_maybe": "Unknown match", + "reloaded_with_new_preferences": "Reloaded with new preferences", + "@reloaded_with_new_preferences": { + "description": "Snackbar title: Shows that the modified settings have been applied" + }, + "@other": {}, + "profile_navbar_label": "Profile", + "@profile_navbar_label": { + "description": "BottomNavigationBarLabel: For the profile and personal preferences page" + }, + "scan_navbar_label": "Search", + "@scan_navbar_label": { + "description": "BottomNavigationBarLabel: For the searching of products" + }, + "history_navbar_label": "History", + "@history_navbar_label": { + "description": "BottomNavigationBarLabel: For the history and compare mode" + }, + "category": "Filter by category", + "@category": { + "description": "From a product list, there's a category filter: this is its title" + }, + "category_all": "All", + "@category_al": { + "description": "Top meta-entry on a category filter" + }, + "category_search": "(category search)", + "filter": "Filter", + "@filter": { + "description": "A button that opens a menu where you can filter within categories. Juices => Apple juices/Orange juices" + }, + "scan": "Scan", + "@scan": { + "description": "Page title: List type: Scanned products" + }, + "search": "Search", + "@search": { + "description": "Hint text of a search text input field" + }, + "tab_for_more": "Tap to see more info...", + "@Product": {}, + "product": "Product", + "@product": {}, + "unknownBrand": "Unknown brand", + "@unknownBrand": {}, + "unknownProductName": "Unknown product name", + "@unknownProductName": {}, + "label_refresh": "Refresh", + "@label_refresh": { + "description": "Refresh the cached product" + }, + "image": "Image", + "front_photo": "Front photo", + "@front_photo": { + "description": "Button label: For adding a picture of the front of a product" + }, + "ingredients": "Ingredients", + "@ingredients": {}, + "ingredients_editing_instructions": "Keep the original order. Indicate the percentage when specified. Separate with a comma or hyphen, use parentheses for ingredients of an ingredient, and indicate allergens between underscores.", + "ingredients_editing_error": "Failed to save the ingredients.", + "ingredients_editing_image_error": "Failed to get a new ingredients image.", + "ingredients_editing_title": "Edit Ingredients", + "ingredients_photo": "Ingredients photo", + "@ingredients_photo": { + "description": "Button label: For adding a picture of the Ingredients of a product" + }, + "nutrition": "Nutrition", + "@nutrition": {}, + "nutrition_page_close_confirmation": "Are you sure you want to go back?", + "nutrition_facts_photo": "Nutrition facts photo", + "@nutrition_facts_photo": { + "description": "Button label: For adding a picture of the nutrition facts of a product" + }, + "packaging_information": "Packaging information", + "@packaging_information": { + "description": "Button label: For adding a picture of the packaging of a product" + }, + "packaging_information_photo": "Packaging information photo", + "@packaging_information_photo": {}, + "missing_product": "You found a new product!", + "@missing_product": {}, + "add_product_take_photos": "Take photos of the packaging to add this product to Open Food Facts", + "@add_product_take_photos": {}, + "add_product_take_photos_descriptive": "Please take some photos and the Open Food Facts engine can work out the rest!", + "@add_product_take_photos_descriptive": {}, + "add_product_information_button_label": "Add product information", + "@add_product_information_button_label": {}, + "new_product": "New Product", + "@new_product": {}, + "front_packaging_photo_button_label": "Front packaging photo", + "@front_packaging_photo_button_label": {}, + "confirm_front_packaging_photo_button_label": "Confirm Front packaging photo", + "@confirm_front_packaging_photo_button_label": { + "description": "Button clicking on which confirms the picture of the front packaging that user just took." + }, + "front_photo_uploaded": "Front photo uploaded", + "@front_photo_uploaded": {}, + "ingredients_photo_button_label": "Ingredients photo", + "@ingredients_photo_button_label": {}, + "confirm_ingredients_photo_button_label": "Confirm Ingredients photo", + "@confirm_ingredients_photo_button_label": { + "description": "Button clicking on which confirms the picture of ingredients that user just took." + }, + "ingredients_photo_uploaded": "Ingredients photo uploaded", + "@ingredients_photo_uploaded": {}, + "nutritional_facts_photo_button_label": "Nutritional facts photo", + "@nutritional_facts_photo_button_label": {}, + "confirm_nutritional_facts_photo_button_label": "Confirm Nutritional facts photo", + "@confirm_nutritional_facts_photo_button_label": { + "description": "Button clicking on which confirms the picture of nutritional facts that user just took." + }, + "nutritional_facts_photo_uploaded": "Nutritional facts photo uploaded", + "@nutritional_facts_photo_uploaded": {}, + "recycling_photo_button_label": "Recycling information photo", + "@recycling_photo_button_label": {}, + "confirm_recycling_photo_button_label": "Confirm Recycling information photo", + "@confirm_recycling_photo_button_label": { + "description": "Button clicking on which confirms the picture of recycling information that user just took." + }, + "recycling_photo_uploaded": "Recycling photo uploaded", + "@recycling_photo_uploaded": {}, + "other_interesting_photo_button_label": "Other interesting photos", + "@other_interesting_photo_button_label": {}, + "confirm_other_interesting_photo_button_label": "Confirm photo", + "@confirm_other_interesting_photo_button_label": { + "description": "Button clicking on which confirms a miscellaneous photo of the product." + }, + "other_photo_uploaded": "Miscellaneous photo uploaded", + "@other_photo_uploaded": {}, + "retake_photo_button_label": "Retake", + "@retake_photo_button_label": { + "description": "Button clicking on which allows users to retake the last photo they took." + }, + "selecting_photo": "Selecting photo", + "@selecting_photo": { + "description": "Progress indicator when the users takes a photo" + }, + "uploading_image": "Uploading Image to the server", + "@uploading_image": { + "description": "Message when a new picture is uploading to the server" + }, + "score_add_missing_ingredients": "Add missing ingredients", + "score_add_missing_nutrition_facts": "Add missing nutrition facts", + "score_add_missing_product_category": "Add missing product category", + "score_update_nutrition_facts": "Update nutrition facts", + "nutrition_page_title": "Product Nutrition Facts", + "nutrition_page_unspecified": "Nutrition facts are not specified on the product", + "nutrition_page_per_100g": "per 100g", + "nutrition_page_per_serving": "per serving", + "nutrition_page_add_nutrient": "Add a nutrient", + "nutrition_page_serving_size": "Serving size", + "nutrition_page_invalid_number": "Invalid number", + "nutrition_page_update_running": "Updating the product on the server...", + "nutrition_page_update_done": "Product updated!", + "more_photos": "More interesting photos", + "@more_photos": {}, + "no_product_found": "No product found", + "@no_product_found": {}, + "not_found": "not found:", + "searchPanelHeader": "Search or scan your first product", + "@Product query status": {}, + "refreshing_product": "Refreshing product", + "@refreshing_product": { + "description": "Confirmation, that the product data of a cached product is queried again" + }, + "product_refreshed": "Product refreshed", + "@product_refreshed": { + "description": "Confirmation, that the product data refresh is done" + }, + "could_not_refresh": "Could not refresh product", + "@could_not_refresh": { + "description": "The product data couldn't be refreshed" + }, + "product_internet_cancel": "Canceled by user.", + "@product_internet_cancel": { + "description": "Confirmation, that the product data got canceled by the user" + }, + "product_internet_error": "Impossible to fetch information about this product due to a network error.", + "cached_results_from": "Show results from:", + "@cached_results_from": { + "description": "Cached results from: x time ago (time ago should not be added to the string)" + }, + "@Product Addition": {}, + "added_product_thanks": "Thank you for adding this product!", + "@added_product_thanks": {}, + "product_search_same_category": "Compare to Category", + "@product_search_same_category": { + "description": "Button looking for the other products within the same category. Less than 30 characters" + }, + "product_improvement_add_category": "Add a category to calculate the Nutri-Score.", + "@product_improvement_add_category": { + "description": "Message for ProductImprovement.ADD_CATEGORY" + }, + "product_improvement_add_nutrition_facts": "Add nutrition facts to calculate the Nutri-Score.", + "@product_improvement_add_nutrition_facts": { + "description": "Message for ProductImprovement.ADD_NUTRITION_FACTS" + }, + "product_improvement_add_nutrition_facts_and_category": "Add nutrition facts and a category to calculate the Nutri-Score.", + "@product_improvement_add_nutrition_facts_and_category": { + "description": "Message for ProductImprovement.ADD_NUTRITION_FACTS_AND_CATEGORY" + }, + "product_improvement_categories_but_no_nutriscore": "The Nutri-Score for this product can't be calculated, which may be due to e.g. a non-standard category. If this is considered an error, please contact us.", + "@product_improvement_categories_but_no_nutriscore": { + "description": "Message for ProductImprovement.CATEGORIES_BUT_NO_NUTRISCORE" + }, + "product_improvement_obsolete_nutrition_image": "The nutrition image is obsolete: please refresh it.", + "@product_improvement_obsolete_nutrition_image": { + "description": "Message for ProductImprovement.OBSOLETE_NUTRITION_IMAGE" + }, + "product_improvement_origins_to_be_completed": "The Eco-Score takes into account the origins of the ingredients. Please take a photo of the ingredient list and/or any geographic claim or edit the product, so they can be taken into account.", + "@product_improvement_origins_to_be_completed": { + "description": "Message for ProductImprovement.ORIGINS_TO_BE_COMPLETED" + }, + "country_chooser_label": "Please choose a country", + "@country_chooser_label": { + "description": "Label shown above a selector where the user can select their country" + }, + "country_selection_explanation": "Some environmental features are location-specific", + "@country_label": { + "description": "Explanation as to why users should select their country." + }, + "product_removed_comparison": "Product removed from comparison", + "@product_removed_comparison": { + "description": "Product got removed from comparison list" + }, + "product_removed_history": "Product removed from history", + "@product_removed_history": { + "description": "Product got removed from history" + }, + "product_could_not_remove": "Could not remove product", + "@product_could_not_remove": { + "description": "Could not remove product from a list" + }, + "@Lists": {}, + "no_prodcut_in_list": "There is no product in this list", + "no_product_in_section": "There is no product in this section", + "recently_seen_products": "History", + "clear": "Clear", + "@clear": { + "description": "Clears a product list" + }, + "really_clear": "Do you really want to delete this list?", + "product_compatibility_unknown": "Compatibility Unknown", + "@product_compatibility_unknown": { + "description": "Product compatibility summary title" + }, + "product_compatibility_incompatible": "Doesn't match", + "@product_compatibility_incompatible": { + "description": "Product compatibility summary title" + }, + "product_compatibility_good": "Good Match", + "@product_compatibility_good": { + "description": "Product compatibility summary title" + }, + "@Plural": {}, + "pct_match": "{percent}% match", + "@pct_match": { + "description": "This product has a x percent match with your preferences", + "placeholders": { + "percent": {} } + }, + "plural_ago_days": "{count,plural, one {} =1{one day ago} other{{count} days ago}}", + "@plural_ago_days": { + "description": "Cached results from: x days ago", + "placeholders": { + "count": {} + } + }, + "plural_ago_hours": "{count,plural, one {} =1{one hour ago} other{{count} hours ago}}", + "@plural_ago_hours": { + "description": "Cached results from: x hours ago", + "placeholders": { + "count": {} + } + }, + "plural_ago_minutes": "{count,plural, one {} =0{less than a minute ago} =1{one minute ago} other{{count} minutes ago}}", + "@plural_ago_minutes": { + "description": "Cached results from: x minutes ago", + "placeholders": { + "count": {} + } + }, + "plural_ago_months": "{count,plural, one {} =1{one month ago} other{{count} months ago}}", + "@plural_ago_months": { + "description": "Cached results from: x months ago", + "placeholders": { + "count": {} + } + }, + "plural_ago_weeks": "{count,plural, one {} =1{one week ago} other{{count} weeks ago}}", + "@plural_ago_weeks": { + "description": "Cached results from: x weeks ago", + "placeholders": { + "count": {} + } + }, + "plural_compare_x_products": "{count,plural, other{Compare {count} Products}", + "@plural_compare_x_products": { + "description": "Button label", + "placeholders": { + "count": {} + } + }, + "compare_products_mode": "Compare", + "@compare_products_mode": { + "description": "Button to switch to 'compare products mode'" + }, + "retry_button_label": "Retry", + "connect_with_us": "Connect with us", + "instagram": "Instagram", + "twitter": "Twitter", + "blog": "Blog", + "faq": "FAQ", + "discover": "Discover", + "how_to_contribute": "How to Contribute", + "main_app_color": "Theme", + "@main_app_color": { + "description": "Heading for the section to pick the main app color" + }, + "hint_knowledge_panel_message": "Your can tap on any part of the card to get more details about what you see. Try it now!", + "@hint_knowledge_panel_message": { + "description": "Hint popup indicating the card is clickable during onboarding" + }, + "consent_analytics_title": "Send anonymous analytics", + "@consent_analytics_title": { + "description": "Title for the consent analytics UI Page" + }, + "consent_analytics_body1": "Help the Open Food Facts volunteer to improve the app. You decide if you want to send anonymous analytics.", + "@conset_analytics_body1": { + "description": "first paragraph for the consent analytics UI Page" + }, + "consent_analytics_body2": "If you change your mind this option can be enabled and disabled at any time from the settings.", + "@consent_analytics_body2": { + "description": "second paragraph for the consent analytics UI Page" + }, + "authorize_button_label": "Authorize", + "@authorize": { + "description": "Button to accept the request of sending the anonymous analytics" + }, + "refuse_button_label": "Refuse", + "@refuse": { + "description": "Button to decline the request of sending the anonymous analytics" + } } \ No newline at end of file diff --git a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart index 327cc6fbdac..3749e038839 100644 --- a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart +++ b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart @@ -9,6 +9,7 @@ import 'package:openfoodfacts/utils/UnitHelper.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/product_query.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_action_button.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; @@ -69,41 +70,45 @@ class _NutritionPageLoadedState extends State { @override Widget build(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context)!; + final AppLocalizations localizations = AppLocalizations.of(context)!; final LocalDatabase localDatabase = context.read(); final List children = []; - children.add(_switchNoNutrition(appLocalizations)); + children.add(_switchNoNutrition(localizations)); if (!_unspecified) { - children.add(_getServingField(appLocalizations)); - children.add(_getServingSwitch(appLocalizations)); + children.add(_getServingField(localizations)); + children.add(_getServingSwitch(localizations)); for (final OrderedNutrient orderedNutrient in _nutritionContainer.getDisplayableNutrients()) { children.add( - _getNutrientRow(appLocalizations, orderedNutrient), + _getNutrientRow(localizations, orderedNutrient), ); } - children.add(_addNutrientButton(appLocalizations)); + children.add(_addNutrientButton(localizations)); } - return Scaffold( - appBar: AppBar( - title: Text(appLocalizations.nutrition_page_title), - actions: [ - IconButton( - onPressed: () => _validateAndSave(localDatabase), - icon: const Icon(Icons.check)) - ], - ), - body: Padding( - padding: const EdgeInsets.symmetric( - horizontal: LARGE_SPACE, - vertical: SMALL_SPACE, + return WillPopScope( + child: Scaffold( + appBar: AppBar( + title: Text(localizations.nutrition_page_title), + actions: [ + IconButton( + onPressed: () => _validateAndSave(localizations, localDatabase), + icon: const Icon(Icons.check), + ) + ], ), - child: Form( - key: _formKey, - child: ListView(children: children), + body: Padding( + padding: const EdgeInsets.symmetric( + horizontal: LARGE_SPACE, + vertical: SMALL_SPACE, + ), + child: Form( + key: _formKey, + child: ListView(children: children), + ), ), ), + onWillPop: () => _showCancelPopup(localizations), ); } @@ -316,11 +321,56 @@ class _NutritionPageLoadedState extends State { label: Text(appLocalizations.nutrition_page_add_nutrient), ); - Future _validateAndSave(final LocalDatabase localDatabase) async { + Future _showCancelPopup(AppLocalizations localizations) async => + await showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: Text(localizations.nutrition_page_close_confirmation), + actions: [ + SmoothActionButton( + text: localizations.yes, + onPressed: () => Navigator.pop(context, true), + ), + SmoothActionButton( + text: localizations.cancel, + onPressed: () => Navigator.pop(context, false), + ) + ], + ), + ) ?? + false; + + Future _validateAndSave(final AppLocalizations localizations, + final LocalDatabase localDatabase) async { if (!_formKey.currentState!.validate()) { return; } - await _save(localDatabase); + + await _showSavePopup(localizations, localDatabase); + } + + Future _showSavePopup( + AppLocalizations localizations, LocalDatabase localDatabase) async { + final bool shouldSave = await showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: Text(localizations.save_confirmation), + actions: [ + SmoothActionButton( + text: localizations.save, + onPressed: () => Navigator.pop(context, true), + ), + SmoothActionButton( + text: localizations.cancel, + onPressed: () => Navigator.pop(context, false), + ) + ], + )) ?? + false; + + if (shouldSave) { + _save(localDatabase); + } } Future _save(final LocalDatabase localDatabase) async { From 71048ebae0d3c269278c08b0617624f3a894e7d9 Mon Sep 17 00:00:00 2001 From: VaiTon Date: Sun, 3 Apr 2022 22:14:23 +0200 Subject: [PATCH 3/3] feat: use material style for dialog --- packages/smooth_app/lib/l10n/app_en.arb | 3 +- .../pages/product/nutrition_page_loaded.dart | 41 +++++++++++-------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 39251faf4f6..85d7d419fd0 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -39,6 +39,7 @@ "@label_web": {}, "learnMore": "Learn more", "@learnMore": {}, + "general_confirmation": "Are you sure?", "incompatible": "Incompatible", "@incompatible": { "description": "Short label for product list view: the product is incompatible with your preferences" @@ -349,7 +350,7 @@ }, "nutrition": "Nutrition", "@nutrition": {}, - "nutrition_page_close_confirmation": "Are you sure you want to go back?", + "nutrition_page_close_confirmation": "Are you sure you want to close without saving?", "nutrition_facts_photo": "Nutrition facts photo", "@nutrition_facts_photo": { "description": "Button label: For adding a picture of the nutrition facts of a product" diff --git a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart index 3749e038839..a696a702840 100644 --- a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart +++ b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart @@ -9,7 +9,6 @@ import 'package:openfoodfacts/utils/UnitHelper.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/product_query.dart'; -import 'package:smooth_app/generic_lib/buttons/smooth_action_button.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; @@ -325,16 +324,20 @@ class _NutritionPageLoadedState extends State { await showDialog( context: context, builder: (BuildContext context) => AlertDialog( - title: Text(localizations.nutrition_page_close_confirmation), - actions: [ - SmoothActionButton( - text: localizations.yes, + shape: const RoundedRectangleBorder( + borderRadius: ROUNDED_BORDER_RADIUS, + ), + title: Text(localizations.general_confirmation), + content: Text(localizations.nutrition_page_close_confirmation), + actions: [ + TextButton( + child: Text(localizations.cancel.toUpperCase()), + onPressed: () => Navigator.pop(context, false), + ), + TextButton( + child: Text(localizations.close.toUpperCase()), onPressed: () => Navigator.pop(context, true), ), - SmoothActionButton( - text: localizations.cancel, - onPressed: () => Navigator.pop(context, false), - ) ], ), ) ?? @@ -354,16 +357,20 @@ class _NutritionPageLoadedState extends State { final bool shouldSave = await showDialog( context: context, builder: (BuildContext context) => AlertDialog( - title: Text(localizations.save_confirmation), - actions: [ - SmoothActionButton( - text: localizations.save, + title: Text(localizations.general_confirmation), + content: Text(localizations.save_confirmation), + shape: const RoundedRectangleBorder( + borderRadius: ROUNDED_BORDER_RADIUS, + ), + actions: [ + TextButton( + child: Text(localizations.cancel.toUpperCase()), + onPressed: () => Navigator.pop(context, false), + ), + TextButton( + child: Text(localizations.save.toUpperCase()), onPressed: () => Navigator.pop(context, true), ), - SmoothActionButton( - text: localizations.cancel, - onPressed: () => Navigator.pop(context, false), - ) ], )) ?? false;