diff --git a/.github/dependabot.yml b/.github/dependabot.yml index dabf854a128..3bd44b0188f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -39,7 +39,6 @@ updates: commit-message: prefix: "chore" include: "scope" - - package-ecosystem: "pub" directory: "/packages/scanner/mlkit/" diff --git a/.github/labeler.yml b/.github/labeler.yml index dd70a37ee65..4fca91ccd05 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -15,7 +15,11 @@ Assets cache: - changed-files: - any-glob-to-any-file: 'packages/smooth_app/lib/background/background_task_manager.dart' - any-glob-to-any-file: 'packages/smooth_app/lib/pages/offline_tasks_page.dart' - + - any-glob-to-any-file: 'packages/smooth_app/lib/background/background_task_crop.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/background/background_task_image.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/background/background_task_upload.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/background/background_task_add_price.dart' + 📚 Documentation: - changed-files: - any-glob-to-any-file: 'README.md' @@ -59,7 +63,6 @@ Web: - any-glob-to-any-file: 'packages/smooth_app/test/widget_test.dart' - any-glob-to-any-file: 'packages/smooth_app/test/basic_test.dart' - any-glob-to-any-file: 'packages/scanner/shared/test/shared_test.dart' - - any-glob-to-any-file: 'packages/smooth_app/test/cache/cache_test.dart' - any-glob-to-any-file: 'packages/smooth_app/integration_test/app_test.dart' - any-glob-to-any-file: 'packages/data_importer/test/data_importer_test.dart' - any-glob-to-any-file: 'packages/scanner/mlkit/test/scanner_mlkit_test.dart' @@ -226,6 +229,9 @@ Generic lib: 🖼️ Photos - Cropping: - changed-files: - any-glob-to-any-file: 'packages/smooth_app/lib/pages/image_crop_page.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/crop_page.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/background/background_task_crop.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/product/product_image_crop_button.dart' User lists: - changed-files: @@ -234,13 +240,49 @@ User lists: Product scan carousel: - changed-files: - - any-glob-to-any-file: 'packages/smooth_app/lib/widgets/smooth_product_carousel.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/scan/carousel/scan_carousel.dart' ✏️ Editing - 📦 Packaging input: - changed-files: - any-glob-to-any-file: 'packages/smooth_app/lib/pages/product/edit_new_packagings.dart' - any-glob-to-any-file: 'packages/smooth_app/lib/pages/product/ocr_packaging_helper.dart' +OCR page: +- changed-files: + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/product/edit_ocr/edit_ocr_main_action.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/product/edit_ocr/edit_ocr_page.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/product/edit_ocr/ocr_ingredients_helper.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/product/edit_ocr/ocr_packaging_helper.dart' + ✏️ Editing - Basic info input: - changed-files: - any-glob-to-any-file: 'packages/smooth_app/lib/pages/product/add_basic_details_page.dart' + +Prices: +- changed-files: + - any-glob-to-any-file: 'packages/smooth_app/lib/background/background_task_add_price.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/data_models/location_list_supplier.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/database/dao_osm_location.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/locations/location_map_page.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/locations/search_location_preloaded_item.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/get_prices_model.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_amount_card.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_amount_field.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_button.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_count_widget.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_currency_card.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_currency_selector.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_data_widget.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_date_card.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_location_card.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_model.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_product_widget.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/prices_page.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/price_proof_card.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/prices_card.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/prices/product_price_add_page.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/onboarding/currency_selector.dart' + - any-glob-to-any-file: 'packages/smooth_app/lib/pages/onboarding/currency_selector_helper.dart' + + + diff --git a/.github/workflows/auto-assign-pr.yml b/.github/workflows/auto-assign-pr.yml index 762822923e9..a41dafbdb97 100644 --- a/.github/workflows/auto-assign-pr.yml +++ b/.github/workflows/auto-assign-pr.yml @@ -16,5 +16,5 @@ jobs: assign-author: runs-on: ubuntu-latest steps: - - uses: toshimaru/auto-author-assign@v2.1.0 + - uses: toshimaru/auto-author-assign@v2.1.1 diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 0756430efb3..8718d5b05ea 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v4 - name: crowdin action - uses: crowdin/github-action@v1.20.2 + uses: crowdin/github-action@v2.0.0 continue-on-error: true with: # Upload sources to Crowdin diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b478ea3793..77f83067321 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,80 @@ # Changelog +## [4.15.0](https://github.com/openfoodfacts/smooth-app/compare/v4.14.0...v4.15.0) (2024-06-28) + + +### Features + +* 5 new icons: chicken / fish / milk / soda happy / soda unhappy ([#5268](https://github.com/openfoodfacts/smooth-app/issues/5268)) ([74cf6c6](https://github.com/openfoodfacts/smooth-app/commit/74cf6c6a2349c5eaa3a599a4556e10aadf39a505)) +* 5079 - new deeplink to the Country Eco-Score ([#5152](https://github.com/openfoodfacts/smooth-app/issues/5152)) ([2ee3cd8](https://github.com/openfoodfacts/smooth-app/commit/2ee3cd840551bc9cc90668b75315cb7cf9b11c67)) +* 5095 - matomo anonymous visitor id now starts with a persistent letter ([#5107](https://github.com/openfoodfacts/smooth-app/issues/5107)) ([f1d0992](https://github.com/openfoodfacts/smooth-app/commit/f1d09923cc0c1c24d6248c4bbc2e34f1eb7469fc)) +* 5099 - new dev mode item to refresh all the products from the server ([#5100](https://github.com/openfoodfacts/smooth-app/issues/5100)) ([c6077dc](https://github.com/openfoodfacts/smooth-app/commit/c6077dce33609e06cd69993cb949d258766d8da5)) +* 5128 - visible dates on raw image grid items ([#5144](https://github.com/openfoodfacts/smooth-app/issues/5144)) ([1922d39](https://github.com/openfoodfacts/smooth-app/commit/1922d39f073c813dde606f9254ca4453279eb56c)) +* 5191 - knowledge panel image card now clickable ([#5220](https://github.com/openfoodfacts/smooth-app/issues/5220)) ([db20b51](https://github.com/openfoodfacts/smooth-app/commit/db20b51cb6dc968a87af2d48956a53fbbdc9536e)) +* 5195 - location search, as a preliminary step ([#5274](https://github.com/openfoodfacts/smooth-app/issues/5274)) ([5a95ca6](https://github.com/openfoodfacts/smooth-app/commit/5a95ca688a99f2e1bb9ac0c7a4718b50227ab2bb)) +* 5195 - new "add one product price" page with background task ([#5292](https://github.com/openfoodfacts/smooth-app/issues/5292)) ([db223b5](https://github.com/openfoodfacts/smooth-app/commit/db223b5db58039d24b6cade67b1ad4a79f8c57bd)) +* 5197 - first product price page ([#5271](https://github.com/openfoodfacts/smooth-app/issues/5271)) ([004ec9c](https://github.com/openfoodfacts/smooth-app/commit/004ec9c411027c9890917d563d970db4d6e1eb5b)) +* 5198 - added a local "latest prices" page ([#5351](https://github.com/openfoodfacts/smooth-app/issues/5351)) ([39c9c43](https://github.com/openfoodfacts/smooth-app/commit/39c9c434b9ebc5a2aef2a85309eb9b8a5ca30a76)) +* 5200 - currency selector ([#5236](https://github.com/openfoodfacts/smooth-app/issues/5236)) ([e3f01ce](https://github.com/openfoodfacts/smooth-app/commit/e3f01ce061b1df91bcdefdc995f4f70c754051a6)) +* 5201 - change currency with country when relevant ([#5238](https://github.com/openfoodfacts/smooth-app/issues/5238)) ([5f7966c](https://github.com/openfoodfacts/smooth-app/commit/5f7966c7f880611d5114876ada463f189cd473b0)) +* 5203 - "add receipt" and "add price tags", even offline or not found ([#5392](https://github.com/openfoodfacts/smooth-app/issues/5392)) ([b9f83c4](https://github.com/openfoodfacts/smooth-app/commit/b9f83c414073e5a0c42b86f3226d6e2ee3c50bad)) +* 5204 - multi-product price addition ([#5375](https://github.com/openfoodfacts/smooth-app/issues/5375)) ([b300955](https://github.com/openfoodfacts/smooth-app/commit/b3009557eca4aba1f184aac1abd56250e36c5067)) +* 5204 - preliminary step for multi-product price addition ([#5367](https://github.com/openfoodfacts/smooth-app/issues/5367)) ([7bf53e8](https://github.com/openfoodfacts/smooth-app/commit/7bf53e894dba429dc9e7ba8d0f2dca4176fd6181)) +* 5205 - added 4 links to prices app ([#5329](https://github.com/openfoodfacts/smooth-app/issues/5329)) ([27259c0](https://github.com/openfoodfacts/smooth-app/commit/27259c0b3c271e30fd299d114ac5ff389f78263f)) +* 5205 - added a "My prices" item linking to the web app ([#5317](https://github.com/openfoodfacts/smooth-app/issues/5317)) ([722c2f4](https://github.com/openfoodfacts/smooth-app/commit/722c2f498d6dbc8190ee9c50def38e9ffc3799c6)) +* 5205 - new "my prices" page ([#5347](https://github.com/openfoodfacts/smooth-app/issues/5347)) ([13072eb](https://github.com/openfoodfacts/smooth-app/commit/13072eb0864d79a5e9775ef42de6fe618816edc0)) +* 5207 - new "My proofs" and "Proof" pages ([#5389](https://github.com/openfoodfacts/smooth-app/issues/5389)) ([4fbf020](https://github.com/openfoodfacts/smooth-app/commit/4fbf0203acb366f6d797b20a4b22f3a2c3a89523)) +* 5301 - added an erasing tool for proofs ([#5341](https://github.com/openfoodfacts/smooth-app/issues/5341)) ([036bda1](https://github.com/openfoodfacts/smooth-app/commit/036bda1fec0371b690b8e4488a5626439bc3c24c)) +* 5301 - price proofs can be cropped and will be displayed ([#5305](https://github.com/openfoodfacts/smooth-app/issues/5305)) ([529fe8f](https://github.com/openfoodfacts/smooth-app/commit/529fe8f5e1e18e9b52782e66e6dd8072f2fed11b)) +* 5318 - added a "price privacy warning" dialog ([#5343](https://github.com/openfoodfacts/smooth-app/issues/5343)) ([e94d61f](https://github.com/openfoodfacts/smooth-app/commit/e94d61fa11ef9e210147f91034431296180a87b7)) +* 5323 - generic way to display product images, with timestamp ([#5333](https://github.com/openfoodfacts/smooth-app/issues/5333)) ([c26528c](https://github.com/openfoodfacts/smooth-app/commit/c26528c9b9a9243800180ade71ac4f5b9b79db6e)) +* 5352 - now opening the related product price page from count button ([#5353](https://github.com/openfoodfacts/smooth-app/issues/5353)) ([536eae4](https://github.com/openfoodfacts/smooth-app/commit/536eae48b1d780e9d77d08c101dec271f32300fd)) +* A feedback form for the prices page ([#5442](https://github.com/openfoodfacts/smooth-app/issues/5442)) ([b110334](https://github.com/openfoodfacts/smooth-app/commit/b110334c3d577683969270baddae6e6d9ad9046b)) +* Add NutriScore V2 into cache + semantics ([#5264](https://github.com/openfoodfacts/smooth-app/issues/5264)) ([d2d8d0a](https://github.com/openfoodfacts/smooth-app/commit/d2d8d0a6df41da1551f9728808b5da8194ef1c08)) +* added deeplinking for the sign up page [#4169](https://github.com/openfoodfacts/smooth-app/issues/4169) ([#5332](https://github.com/openfoodfacts/smooth-app/issues/5332)) ([2e44edb](https://github.com/openfoodfacts/smooth-app/commit/2e44edb5669eedfb8331d6b7fd06a8dacc2172be)) +* Better error message for search screen ([#5298](https://github.com/openfoodfacts/smooth-app/issues/5298)) ([4b6ed1e](https://github.com/openfoodfacts/smooth-app/commit/4b6ed1e8b66e64d0af7a49f7f3ae13afab35c28a)) +* Crop page: increase paddings ([#5279](https://github.com/openfoodfacts/smooth-app/issues/5279)) ([ebb90b1](https://github.com/openfoodfacts/smooth-app/commit/ebb90b171a87657b7ab6ac5f8aefca74211908ea)) +* currency symbols ([#5311](https://github.com/openfoodfacts/smooth-app/issues/5311)) ([4db1d6c](https://github.com/openfoodfacts/smooth-app/commit/4db1d6c25cbb4b2f4b2b24c201987d4eb9066536)) +* Custom domain + environment support for the TagLine ([#5364](https://github.com/openfoodfacts/smooth-app/issues/5364)) ([08071d2](https://github.com/openfoodfacts/smooth-app/commit/08071d273b048753b1a1c1d9ada51d224c60873c)) +* Extract ingredients/packaging: loading / loaded / extracting states ([#5384](https://github.com/openfoodfacts/smooth-app/issues/5384)) ([65ce9c4](https://github.com/openfoodfacts/smooth-app/commit/65ce9c45ce40ab6f2caa7dfadc57be4092174e37)) +* Guide for Nutri-Score V2 ([#5273](https://github.com/openfoodfacts/smooth-app/issues/5273)) ([dbd23f5](https://github.com/openfoodfacts/smooth-app/commit/dbd23f50f8b3a00974ffd37bf3ce491b41aad9b1)) +* Improve photo gallery accessibility + internationalization ([#5366](https://github.com/openfoodfacts/smooth-app/issues/5366)) ([64d38f1](https://github.com/openfoodfacts/smooth-app/commit/64d38f17dafb715499857fc4611bb3f25cdbb524)) +* Inject all colors from palette in an extension ([#5267](https://github.com/openfoodfacts/smooth-app/issues/5267)) ([f9797bc](https://github.com/openfoodfacts/smooth-app/commit/f9797bc8325f9300954cf0cbfb9f7d6aed8c4cac)) +* Knowledge Panel details page accessibility improvements ([#5290](https://github.com/openfoodfacts/smooth-app/issues/5290)) ([44e4774](https://github.com/openfoodfacts/smooth-app/commit/44e47749367d74b7b9cf19a795a848f6e9471b88)) +* OCR: Better explain why the photo is still loading ([#5426](https://github.com/openfoodfacts/smooth-app/issues/5426)) ([ad9c06e](https://github.com/openfoodfacts/smooth-app/commit/ad9c06eaf8d69989afde97181ef5ac1c885161f5)) +* prices - barcode reader for additional products ([#5381](https://github.com/openfoodfacts/smooth-app/issues/5381)) ([35a4ab0](https://github.com/openfoodfacts/smooth-app/commit/35a4ab03c027406af6946ed024f7d53c3e86d322)) +* prices - top contributors now within the app ([#5383](https://github.com/openfoodfacts/smooth-app/issues/5383)) ([7809854](https://github.com/openfoodfacts/smooth-app/commit/780985462a773ca63fd7db8ed101541347fd3441)) +* Snap scrolling for guides ([#5283](https://github.com/openfoodfacts/smooth-app/issues/5283)) ([8338e90](https://github.com/openfoodfacts/smooth-app/commit/8338e90aac218441e168c376db3fd105b50e8324)) +* Spellchecker for OCR screens ([#5409](https://github.com/openfoodfacts/smooth-app/issues/5409)) ([f6ec9df](https://github.com/openfoodfacts/smooth-app/commit/f6ec9dfee59f2a622ee109eea90f1b504dfddf3d)) +* Tagline V3 ([#5350](https://github.com/openfoodfacts/smooth-app/issues/5350)) ([41abf73](https://github.com/openfoodfacts/smooth-app/commit/41abf730fe09139ee68ce765eff00a72f47d9999)) + + +### Bug Fixes + +* `ThemeProvider` properly resync the theme ([#5363](https://github.com/openfoodfacts/smooth-app/issues/5363)) ([b5b308c](https://github.com/openfoodfacts/smooth-app/commit/b5b308c99d8d73ac7218efd82c839a00348454b5)) +* 2 bugfixes for the tagline (empty content + image in error) ([#5421](https://github.com/openfoodfacts/smooth-app/issues/5421)) ([4deb0b4](https://github.com/openfoodfacts/smooth-app/commit/4deb0b42a47db7dee4cb8025a20ad3e6c29fed03)) +* 4957 - SVG icons are now correctly refreshed when their URL change ([#5133](https://github.com/openfoodfacts/smooth-app/issues/5133)) ([6ac205c](https://github.com/openfoodfacts/smooth-app/commit/6ac205cc4b8adb8c49a1cbb5a4c31a20edfbe983)) +* 5102 - fixed the path of the database to backup ([#5103](https://github.com/openfoodfacts/smooth-app/issues/5103)) ([cd563f5](https://github.com/openfoodfacts/smooth-app/commit/cd563f5d12627f86d253d6d6737a6d9b83342147)) +* 5104 - fixed the position of the "Failed lookup" string ([#5105](https://github.com/openfoodfacts/smooth-app/issues/5105)) ([98de444](https://github.com/openfoodfacts/smooth-app/commit/98de4446344d825aa9aae5ee397c65b4ec16da44)) +* 5121 - more relevant choice of nutrients to display (edit page) ([#5150](https://github.com/openfoodfacts/smooth-app/issues/5150)) ([3217dfe](https://github.com/openfoodfacts/smooth-app/commit/3217dfe63e6cd51cc98b599c327694a936ab81ed)) +* 5145 - bold style for unknown ingredients in KP ([#5149](https://github.com/openfoodfacts/smooth-app/issues/5149)) ([0e3eee2](https://github.com/openfoodfacts/smooth-app/commit/0e3eee2c3ed2aa6482a3287005f5fa58a7223576)) +* 5217 - no check on cookie as it's always null ([#5222](https://github.com/openfoodfacts/smooth-app/issues/5222)) ([d744fab](https://github.com/openfoodfacts/smooth-app/commit/d744fab3344fed5e86caf9be47c01429d18356d9)) +* 5221 - downgrade to previous connectivity_plus package version ([#5223](https://github.com/openfoodfacts/smooth-app/issues/5223)) ([2594b38](https://github.com/openfoodfacts/smooth-app/commit/2594b38fec4b2fa470b6cd0e80acaaa7cfe4729d)) +* 5247 - now "unselect"ing for the correct language ([#5266](https://github.com/openfoodfacts/smooth-app/issues/5266)) ([8758b0e](https://github.com/openfoodfacts/smooth-app/commit/8758b0e818922d7adc1579deac37fb0b41cd86c0)) +* 5288 - added svg files to cache ([#5299](https://github.com/openfoodfacts/smooth-app/issues/5299)) ([dcd70ea](https://github.com/openfoodfacts/smooth-app/commit/dcd70eacc417f48daa3870fae6dceb5e78408a11)) +* 5330 - updated link to the skill pool ([#5334](https://github.com/openfoodfacts/smooth-app/issues/5334)) ([1ce24e8](https://github.com/openfoodfacts/smooth-app/commit/1ce24e8f2d31dbf96b77c2a0530dfcaf09e8a20d)) +* 5345 ([#5346](https://github.com/openfoodfacts/smooth-app/issues/5346)) ([2dd4575](https://github.com/openfoodfacts/smooth-app/commit/2dd4575b96baa33fb659bc4fd56478906445740f)) +* 5371 - less ambiguous currency symbol ([#5376](https://github.com/openfoodfacts/smooth-app/issues/5376)) ([18505dd](https://github.com/openfoodfacts/smooth-app/commit/18505ddc0e09ec7bf6f048dd7b0f1eb7a33a04db)) +* adjusting onTap call ([#5113](https://github.com/openfoodfacts/smooth-app/issues/5113)) ([f57c0c0](https://github.com/openfoodfacts/smooth-app/commit/f57c0c00c72f341595d036a7f94bc9c1d8951520)) +* Fix issues with themes ([#5410](https://github.com/openfoodfacts/smooth-app/issues/5410)) ([6026fbc](https://github.com/openfoodfacts/smooth-app/commit/6026fbc91bdad67d84cdffd8c8cbefa1b15914aa)) +* Fix performances on the photos gallery ([#5447](https://github.com/openfoodfacts/smooth-app/issues/5447)) ([1c4b959](https://github.com/openfoodfacts/smooth-app/commit/1c4b95954eda5150fbdb846afa94be8f65ca1d1e)) +* Guide: Rewrite newline character from translations ([#5365](https://github.com/openfoodfacts/smooth-app/issues/5365)) ([f5973da](https://github.com/openfoodfacts/smooth-app/commit/f5973da76964a31a498e1e546f8566948b3b44fa)) +* Prices feedback form missing SafeArea-like ([#5448](https://github.com/openfoodfacts/smooth-app/issues/5448)) ([08a8df1](https://github.com/openfoodfacts/smooth-app/commit/08a8df14f464a6abbe5ec92b99bd84ab6d310687)) +* RankingFloatingActionButton partially off-screen for some translations ([#5117](https://github.com/openfoodfacts/smooth-app/issues/5117)) ([9075fdc](https://github.com/openfoodfacts/smooth-app/commit/9075fdc3430fbb37351f8e42992feaca36e62927)) +* Support for photos in a different language than the default one (green buttons) ([#5278](https://github.com/openfoodfacts/smooth-app/issues/5278)) ([ffe90e7](https://github.com/openfoodfacts/smooth-app/commit/ffe90e76528d1dcb3731ddd87ec4d6871c18a7d1)) +* Update dependabot.yml ([49998ec](https://github.com/openfoodfacts/smooth-app/commit/49998eca6a20663604eff5d6592c4681f2468ad4)) +* Update tagline_provider.dart ([e4cb2f2](https://github.com/openfoodfacts/smooth-app/commit/e4cb2f26a10cf7a8b269c03bfadb82ccaef40b4e)) + ## [4.14.0](https://github.com/openfoodfacts/smooth-app/compare/v4.13.0...v4.14.0) (2024-03-02) diff --git a/README.md b/README.md index 1e8c7155538..d8a99c424d2 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,9 @@ ## Current Release -- Latest commit deployed to Apple App Store: Released on Nov 29th as Build 835 -- Latest commit deployed to PlayStore: 792 from Nov 6th 11AM +- Latest commit deployed to Apple App Store: Released on Feb 26 2024 as Version 4.13.1 +- Latest commit deployed to PlayStore: 1379 Released on Feb 24 2024 9:42 PM +- Latest commit deployed to F-Droid: 9 March 2024 as Version 4.14.0 (1280) ## Presentation @@ -87,7 +88,7 @@ - Make sure you have installed flutter and all the requirements - [Official flutter installation guide](https://docs.flutter.dev/get-started/install) -- Currently, the app uses the latest stable version of Flutter (3.19.x). +- Currently, the app uses the following version of Flutter (3.22.x). We have predefined run configurations for Android Studio and Visual Studio Code diff --git a/flutter-version.txt b/flutter-version.txt index 0bc8e967b83..dbc9520ba41 100644 --- a/flutter-version.txt +++ b/flutter-version.txt @@ -1 +1 @@ -3.19.5 \ No newline at end of file +3.22.2 \ No newline at end of file diff --git a/packages/app_store/apple_app_store/pubspec.yaml b/packages/app_store/apple_app_store/pubspec.yaml index c3317039f29..0a6b0494448 100644 --- a/packages/app_store/apple_app_store/pubspec.yaml +++ b/packages/app_store/apple_app_store/pubspec.yaml @@ -4,13 +4,13 @@ version: 0.0.1 publish_to: "none" environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.4.0 <4.0.0' dependencies: flutter: sdk: flutter - in_app_review: 2.0.4 + in_app_review: 2.0.9 app_store_shared: path: ../shared @@ -18,6 +18,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: 2.0.2 + flutter_lints: 2.0.3 openfoodfacts_flutter_lints: git: https://github.com/openfoodfacts/openfoodfacts_flutter_lints.git \ No newline at end of file diff --git a/packages/app_store/google_play/pubspec.yaml b/packages/app_store/google_play/pubspec.yaml index e906b0e2f76..54161b2443e 100644 --- a/packages/app_store/google_play/pubspec.yaml +++ b/packages/app_store/google_play/pubspec.yaml @@ -4,13 +4,13 @@ version: 0.0.1 publish_to: "none" environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.4.0 <4.0.0' dependencies: flutter: sdk: flutter - in_app_review: 2.0.4 + in_app_review: 2.0.9 app_store_shared: path: ../shared @@ -18,7 +18,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: 2.0.2 + flutter_lints: 2.0.3 openfoodfacts_flutter_lints: git: https://github.com/openfoodfacts/openfoodfacts_flutter_lints.git diff --git a/packages/app_store/shared/pubspec.yaml b/packages/app_store/shared/pubspec.yaml index ea7e215339c..86fec92bc7f 100644 --- a/packages/app_store/shared/pubspec.yaml +++ b/packages/app_store/shared/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0 publish_to: "none" environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.4.0 <4.0.0' dependencies: flutter: @@ -13,7 +13,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: 2.0.2 + flutter_lints: 2.0.3 openfoodfacts_flutter_lints: git: https://github.com/openfoodfacts/openfoodfacts_flutter_lints.git diff --git a/packages/app_store/uri_store/pubspec.yaml b/packages/app_store/uri_store/pubspec.yaml index 7ed3f6a6611..1e661bdb92a 100644 --- a/packages/app_store/uri_store/pubspec.yaml +++ b/packages/app_store/uri_store/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 publish_to: "none" environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.4.0 <4.0.0' dependencies: flutter: @@ -18,6 +18,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: 2.0.2 + flutter_lints: 2.0.3 openfoodfacts_flutter_lints: git: https://github.com/openfoodfacts/openfoodfacts_flutter_lints.git \ No newline at end of file diff --git a/packages/scanner/ml_kit/pubspec.yaml b/packages/scanner/ml_kit/pubspec.yaml index 5b69d47d97a..4dca15bc983 100644 --- a/packages/scanner/ml_kit/pubspec.yaml +++ b/packages/scanner/ml_kit/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0 publish_to: "none" environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.4.0 <4.0.0' dependencies: flutter: @@ -13,7 +13,7 @@ dependencies: visibility_detector: 0.4.0+2 async: 2.11.0 - mobile_scanner: 3.5.2 + mobile_scanner: 4.0.1 scanner_shared: path: ../shared diff --git a/packages/scanner/shared/lib/src/scanner_visor.dart b/packages/scanner/shared/lib/src/scanner_visor.dart index 3eb78bf710f..15ebb165c89 100644 --- a/packages/scanner/shared/lib/src/scanner_visor.dart +++ b/packages/scanner/shared/lib/src/scanner_visor.dart @@ -160,23 +160,27 @@ class VisorButton extends StatelessWidget { @override Widget build(BuildContext context) { - return Material( - type: MaterialType.transparency, - child: InkWell( - onTap: onTap, - borderRadius: const BorderRadius.all( - Radius.circular( - SmoothBarcodeScannerVisor.CORNER_PADDING, + return Semantics( + label: tooltip, + excludeSemantics: true, + child: Material( + type: MaterialType.transparency, + child: InkWell( + onTap: onTap, + borderRadius: const BorderRadius.all( + Radius.circular( + SmoothBarcodeScannerVisor.CORNER_PADDING, + ), ), - ), - child: Tooltip( - message: tooltip, - enableFeedback: true, - child: Padding( - padding: const EdgeInsets.all(12.0), - child: IconTheme( - data: const IconThemeData(color: Colors.white), - child: child, + child: Tooltip( + message: tooltip, + enableFeedback: true, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: IconTheme( + data: const IconThemeData(color: Colors.white), + child: child, + ), ), ), ), diff --git a/packages/scanner/shared/pubspec.yaml b/packages/scanner/shared/pubspec.yaml index 7730dc8abaf..7fa9a28d1f0 100644 --- a/packages/scanner/shared/pubspec.yaml +++ b/packages/scanner/shared/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0 publish_to: "none" environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.4.0 <4.0.0' dependencies: flutter: diff --git a/packages/scanner/zxing/pubspec.yaml b/packages/scanner/zxing/pubspec.yaml index 76b0988cd50..0e32939a7b4 100644 --- a/packages/scanner/zxing/pubspec.yaml +++ b/packages/scanner/zxing/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.0.0 publish_to: "none" environment: - sdk: '>=3.3.0 <4.0.0' + sdk: '>=3.4.0 <4.0.0' dependencies: flutter: diff --git a/packages/smooth_app/android/Gemfile.lock b/packages/smooth_app/android/Gemfile.lock index f1f735f4275..c2fcdce8648 100644 --- a/packages/smooth_app/android/Gemfile.lock +++ b/packages/smooth_app/android/Gemfile.lock @@ -16,17 +16,17 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.907.0) - aws-sdk-core (3.191.6) + aws-partitions (1.945.0) + aws-sdk-core (3.197.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.78.0) - aws-sdk-core (~> 3, >= 3.191.0) + aws-sdk-kms (1.85.0) + aws-sdk-core (~> 3, >= 3.197.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.146.1) - aws-sdk-core (~> 3, >= 3.191.0) + aws-sdk-s3 (1.152.3) + aws-sdk-core (~> 3, >= 3.197.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) aws-sigv4 (1.8.0) @@ -74,7 +74,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.220.0) + fastlane (2.221.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -155,31 +155,32 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.5) + http-cookie (1.0.6) domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) - jwt (2.8.1) + jwt (2.8.2) base64 - mini_magick (4.12.0) + mini_magick (4.13.1) mini_mime (1.1.5) multi_json (1.15.0) - multipart-post (2.4.0) + multipart-post (2.4.1) nanaimo (0.3.0) naturally (2.2.1) nkf (0.2.0) - optparse (0.4.0) + optparse (0.5.0) os (1.1.4) plist (3.7.1) - public_suffix (5.0.5) - rake (13.2.0) + public_suffix (5.1.1) + rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.6) + rexml (3.2.9) + strscan rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -192,6 +193,7 @@ GEM simctl (1.6.10) CFPropertyList naturally + strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) diff --git a/packages/smooth_app/assets/animations/nutriscore.riv b/packages/smooth_app/assets/animations/nutriscore.riv index 8ac9e8b4caf..4dec97e4360 100644 Binary files a/packages/smooth_app/assets/animations/nutriscore.riv and b/packages/smooth_app/assets/animations/nutriscore.riv differ diff --git a/packages/smooth_app/assets/animations/off.riv b/packages/smooth_app/assets/animations/off.riv index de111e2ade5..f3d36c7ff93 100644 Binary files a/packages/smooth_app/assets/animations/off.riv and b/packages/smooth_app/assets/animations/off.riv differ diff --git a/packages/smooth_app/assets/app/logo_text_black.svg b/packages/smooth_app/assets/app/logo_text_black.svg new file mode 100644 index 00000000000..00d802eefe4 --- /dev/null +++ b/packages/smooth_app/assets/app/logo_text_black.svg @@ -0,0 +1,45 @@ + + + Logo OFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/app/logo_text_white.svg b/packages/smooth_app/assets/app/logo_text_white.svg new file mode 100644 index 00000000000..098faa7b514 --- /dev/null +++ b/packages/smooth_app/assets/app/logo_text_white.svg @@ -0,0 +1,45 @@ + + + Logo OFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/info.svg b/packages/smooth_app/assets/cache/info.svg new file mode 100644 index 00000000000..25cf1804ee4 --- /dev/null +++ b/packages/smooth_app/assets/cache/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/nutriscore-a-new-de.svg b/packages/smooth_app/assets/cache/nutriscore-a-new-de.svg new file mode 100644 index 00000000000..dd477c13d49 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-a-new-de.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-a-new-en.svg b/packages/smooth_app/assets/cache/nutriscore-a-new-en.svg new file mode 100644 index 00000000000..7ff414602af --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-a-new-en.svg @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-a-new-formula-en.svg b/packages/smooth_app/assets/cache/nutriscore-a-new-formula-en.svg deleted file mode 100644 index 6516f55c498..00000000000 --- a/packages/smooth_app/assets/cache/nutriscore-a-new-formula-en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/nutriscore-a-new-fr.svg b/packages/smooth_app/assets/cache/nutriscore-a-new-fr.svg new file mode 100644 index 00000000000..98eadef0fd0 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-a-new-fr.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-a-new-lb.svg b/packages/smooth_app/assets/cache/nutriscore-a-new-lb.svg new file mode 100644 index 00000000000..eccf6122864 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-a-new-lb.svg @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-a-new-nl.svg b/packages/smooth_app/assets/cache/nutriscore-a-new-nl.svg new file mode 100644 index 00000000000..2f42b5a3c33 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-a-new-nl.svg @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-b-new-de.svg b/packages/smooth_app/assets/cache/nutriscore-b-new-de.svg new file mode 100644 index 00000000000..526c9fd6563 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-b-new-de.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-b-new-en.svg b/packages/smooth_app/assets/cache/nutriscore-b-new-en.svg new file mode 100644 index 00000000000..0b814f08a42 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-b-new-en.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-b-new-formula-en.svg b/packages/smooth_app/assets/cache/nutriscore-b-new-formula-en.svg deleted file mode 100644 index f973754dc27..00000000000 --- a/packages/smooth_app/assets/cache/nutriscore-b-new-formula-en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/nutriscore-b-new-fr.svg b/packages/smooth_app/assets/cache/nutriscore-b-new-fr.svg new file mode 100644 index 00000000000..b69eecab2e6 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-b-new-fr.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-b-new-lb.svg b/packages/smooth_app/assets/cache/nutriscore-b-new-lb.svg new file mode 100644 index 00000000000..400cb6cd1ea --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-b-new-lb.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-b-new-nl.svg b/packages/smooth_app/assets/cache/nutriscore-b-new-nl.svg new file mode 100644 index 00000000000..da157bbb357 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-b-new-nl.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-c-new-de.svg b/packages/smooth_app/assets/cache/nutriscore-c-new-de.svg new file mode 100644 index 00000000000..4c754b3b352 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-c-new-de.svg @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-c-new-en.svg b/packages/smooth_app/assets/cache/nutriscore-c-new-en.svg new file mode 100644 index 00000000000..ccdfce3d122 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-c-new-en.svg @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-c-new-formula-en.svg b/packages/smooth_app/assets/cache/nutriscore-c-new-formula-en.svg deleted file mode 100644 index 7e208163e3f..00000000000 --- a/packages/smooth_app/assets/cache/nutriscore-c-new-formula-en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/nutriscore-c-new-fr.svg b/packages/smooth_app/assets/cache/nutriscore-c-new-fr.svg new file mode 100644 index 00000000000..71587caf799 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-c-new-fr.svg @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-c-new-lb.svg b/packages/smooth_app/assets/cache/nutriscore-c-new-lb.svg new file mode 100644 index 00000000000..bbb3191232e --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-c-new-lb.svg @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-c-new-nl.svg b/packages/smooth_app/assets/cache/nutriscore-c-new-nl.svg new file mode 100644 index 00000000000..66e0cbd75e0 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-c-new-nl.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-d-new-de.svg b/packages/smooth_app/assets/cache/nutriscore-d-new-de.svg new file mode 100644 index 00000000000..875a6a7a69f --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-d-new-de.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-d-new-en.svg b/packages/smooth_app/assets/cache/nutriscore-d-new-en.svg new file mode 100644 index 00000000000..1ee2a792e94 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-d-new-en.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-d-new-formula-en.svg b/packages/smooth_app/assets/cache/nutriscore-d-new-formula-en.svg deleted file mode 100644 index 8a0ff49d8cd..00000000000 --- a/packages/smooth_app/assets/cache/nutriscore-d-new-formula-en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/nutriscore-d-new-fr.svg b/packages/smooth_app/assets/cache/nutriscore-d-new-fr.svg new file mode 100644 index 00000000000..cbf15b2395d --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-d-new-fr.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-d-new-lb.svg b/packages/smooth_app/assets/cache/nutriscore-d-new-lb.svg new file mode 100644 index 00000000000..9ecc130b830 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-d-new-lb.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-d-new-nl.svg b/packages/smooth_app/assets/cache/nutriscore-d-new-nl.svg new file mode 100644 index 00000000000..649654c3cba --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-d-new-nl.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-e-new-de.svg b/packages/smooth_app/assets/cache/nutriscore-e-new-de.svg new file mode 100644 index 00000000000..6d9bbce4d97 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-e-new-de.svg @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-e-new-en.svg b/packages/smooth_app/assets/cache/nutriscore-e-new-en.svg new file mode 100644 index 00000000000..7eecf5f2a66 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-e-new-en.svg @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-e-new-formula-en.svg b/packages/smooth_app/assets/cache/nutriscore-e-new-formula-en.svg deleted file mode 100644 index f6c74eda004..00000000000 --- a/packages/smooth_app/assets/cache/nutriscore-e-new-formula-en.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/nutriscore-e-new-fr.svg b/packages/smooth_app/assets/cache/nutriscore-e-new-fr.svg new file mode 100644 index 00000000000..6d0dcc74a00 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-e-new-fr.svg @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-e-new-lb.svg b/packages/smooth_app/assets/cache/nutriscore-e-new-lb.svg new file mode 100644 index 00000000000..481d624a659 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-e-new-lb.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-e-new-nl.svg b/packages/smooth_app/assets/cache/nutriscore-e-new-nl.svg new file mode 100644 index 00000000000..cfffd912cb9 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-e-new-nl.svg @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-formula-en.svg b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-de.svg similarity index 100% rename from packages/smooth_app/assets/cache/nutriscore-not-applicable-new-formula-en.svg rename to packages/smooth_app/assets/cache/nutriscore-not-applicable-new-de.svg diff --git a/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-en.svg b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-en.svg new file mode 100644 index 00000000000..bcb99574253 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-en.svg @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-fr.svg b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-fr.svg new file mode 100644 index 00000000000..bcb99574253 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-fr.svg @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-lb.svg b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-lb.svg new file mode 100644 index 00000000000..bcb99574253 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-lb.svg @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-nl.svg b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-nl.svg new file mode 100644 index 00000000000..bcb99574253 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-not-applicable-new-nl.svg @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-unknown-new-formula-en.svg b/packages/smooth_app/assets/cache/nutriscore-unknown-new-de.svg similarity index 100% rename from packages/smooth_app/assets/cache/nutriscore-unknown-new-formula-en.svg rename to packages/smooth_app/assets/cache/nutriscore-unknown-new-de.svg diff --git a/packages/smooth_app/assets/cache/nutriscore-unknown-new-en.svg b/packages/smooth_app/assets/cache/nutriscore-unknown-new-en.svg new file mode 100644 index 00000000000..407614599c9 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-unknown-new-en.svg @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-unknown-new-fr.svg b/packages/smooth_app/assets/cache/nutriscore-unknown-new-fr.svg new file mode 100644 index 00000000000..407614599c9 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-unknown-new-fr.svg @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-unknown-new-lb.svg b/packages/smooth_app/assets/cache/nutriscore-unknown-new-lb.svg new file mode 100644 index 00000000000..407614599c9 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-unknown-new-lb.svg @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/nutriscore-unknown-new-nl.svg b/packages/smooth_app/assets/cache/nutriscore-unknown-new-nl.svg new file mode 100644 index 00000000000..407614599c9 --- /dev/null +++ b/packages/smooth_app/assets/cache/nutriscore-unknown-new-nl.svg @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/off-logo-icon-light.svg b/packages/smooth_app/assets/cache/off-logo-icon-light.svg new file mode 100644 index 00000000000..f32149e7d50 --- /dev/null +++ b/packages/smooth_app/assets/cache/off-logo-icon-light.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/smooth_app/assets/cache/points-negative-0-20.svg b/packages/smooth_app/assets/cache/points-negative-0-20.svg new file mode 100644 index 00000000000..f58faf6c0cf --- /dev/null +++ b/packages/smooth_app/assets/cache/points-negative-0-20.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/points-negative-15-15.svg b/packages/smooth_app/assets/cache/points-negative-15-15.svg new file mode 100644 index 00000000000..8f42909676b --- /dev/null +++ b/packages/smooth_app/assets/cache/points-negative-15-15.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/points-negative-3-10.svg b/packages/smooth_app/assets/cache/points-negative-3-10.svg new file mode 100644 index 00000000000..1fcccef28c2 --- /dev/null +++ b/packages/smooth_app/assets/cache/points-negative-3-10.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/points-negative-5-10.svg b/packages/smooth_app/assets/cache/points-negative-5-10.svg new file mode 100644 index 00000000000..256d050ea77 --- /dev/null +++ b/packages/smooth_app/assets/cache/points-negative-5-10.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/points-positive-0-5.svg b/packages/smooth_app/assets/cache/points-positive-0-5.svg new file mode 100644 index 00000000000..770a16f3a0f --- /dev/null +++ b/packages/smooth_app/assets/cache/points-positive-0-5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/smooth_app/assets/cache/signalconso.png b/packages/smooth_app/assets/cache/signalconso.png new file mode 100644 index 00000000000..8c9cc70bf6b Binary files /dev/null and b/packages/smooth_app/assets/cache/signalconso.png differ diff --git a/packages/smooth_app/assets/fonts/SmoothIcons.ttf b/packages/smooth_app/assets/fonts/SmoothIcons.ttf index 7aabb31d856..7765d978af5 100644 Binary files a/packages/smooth_app/assets/fonts/SmoothIcons.ttf and b/packages/smooth_app/assets/fonts/SmoothIcons.ttf differ diff --git a/packages/smooth_app/assets/fonts/icons/chicken.svg b/packages/smooth_app/assets/fonts/icons/chicken.svg new file mode 100644 index 00000000000..6e901f76b04 --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/chicken.svg @@ -0,0 +1,7 @@ + + + Chicken + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/config.json b/packages/smooth_app/assets/fonts/icons/config.json index 4c33fa29e62..e8c66e0f517 100644 --- a/packages/smooth_app/assets/fonts/icons/config.json +++ b/packages/smooth_app/assets/fonts/icons/config.json @@ -739,6 +739,132 @@ "search": [ "qrcode_corners" ] + }, + { + "uid": "dfaf7621276027ab1c05f0d86bf8c486", + "css": "chicken", + "code": 59392, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M641.7 0C604.9 0 575 29.8 575 66.7 575 95.2 593 119.3 618.2 128.9 648.2 90.9 694.7 66.7 745.7 66.7 783.7 66.7 818.6 79.9 846.4 101.8 857 93.7 863.9 81 863.9 66.7 863.9 42.1 844 22.2 819.5 22.2 809 22.2 799.5 26 791.9 32.1 783 13.2 763.9 0 741.7 0 721.7 0 704.3 10.7 694.5 26.5 682.4 10.5 663.3 0 641.7 0ZM745.7 111.1C687.2 111.1 636.9 154.8 628.7 212.6L602.4 396.5C587.1 503.5 473.3 575.8 375 577.7L375 533.3C375 484.3 335.1 444.4 286.1 444.4 259.6 444.4 235.7 456.1 219.4 474.6L219.4 466.7C219.4 417.6 179.6 377.8 130.6 377.8 81.5 377.8 41.7 417.6 41.7 466.7L41.7 588.9C41.7 831 206.2 1000 441.7 1000 530.2 1000 610.6 975.3 677 932.2 717.3 887.2 731.1 826.5 713.3 767.7 697.3 714.8 658.7 674.9 615.1 666.2 603 663.8 595.2 652.1 597.7 640.1 600 628.1 611.8 620.2 623.8 622.7 684.2 634.7 734.8 685.4 755.8 754.9 766.4 789.8 767.7 825.2 761.3 858.9 825.6 785.3 863.9 687.9 863.9 577.8L863.9 377.8 941.9 299.8C948.9 292.8 952.8 283.3 952.8 273.5 952.8 256.4 941.2 241.5 924.6 237.4L863.9 222.2 863.5 225.4C861.4 162.1 809.5 111.1 745.7 111.1ZM741.7 222.2C760.1 222.2 775 237.2 775 255.6 775 274 760.1 288.9 741.7 288.9 723.3 288.9 708.3 274 708.3 255.6 708.3 237.2 723.3 222.2 741.7 222.2Z", + "width": 1000 + }, + "search": [ + "chicken" + ] + }, + { + "uid": "47c85b2e17036ec1c3d43e80af34f97a", + "css": "fish", + "code": 59458, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M997.4 508.4C994.6 500.1 928.8 314.9 709.9 256 694.6 189.9 641.1 138 571.6 126.3L388.8 95.9C372.7 93.2 356.4 99 345.5 111.1 334.6 123.2 330.7 140.1 335.1 155.8L371.1 283.2C322.6 304.6 276.4 332.8 238.4 364 201.5 330 143.7 285.7 47.6 285.7 21.3 285.7 0 307.1 0 333.3L0 714.3C0 740.6 21.3 761.9 47.6 761.9 136.8 761.9 197.4 715.9 236 683 274 716 338.9 761.8 428.6 788.2L428.6 857.1C428.6 871.1 434.7 884.4 445.3 893.4 454 900.8 465 904.8 476.2 904.8 478.7 904.8 481.3 904.6 483.9 904.1 499.5 901.6 628.4 878.3 686.3 796.2 912.6 743.6 993.1 550.1 996.5 541.6 1000.8 531 1001.1 519.2 997.4 508.4ZM690.5 428.6C730 428.6 761.9 460.5 761.9 500 761.9 539.5 730 571.4 690.5 571.4 651 571.4 619 539.5 619 500 619 460.5 651 428.6 690.5 428.6Z", + "width": 1000 + }, + "search": [ + "fish" + ] + }, + { + "uid": "3a5aae672b133ef2885423db0fb2605f", + "css": "milk", + "code": 59459, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M283.5 0C263.3 0 247 16.4 247 36.6L247 76.7 139.1 292.3C129.8 310.9 125 331.5 125 352.3L125 914.6C125 961.3 163.7 1000 210.4 1000L771.3 1000C818 1000 856.7 961.3 856.7 914.6L856.7 352.3C856.7 331.4 851.8 310.9 842.6 292.3 842.6 292.3 842.6 292.3 842.6 292.3L734.8 76.7 734.8 36.6C734.8 16.4 718.4 0 698.2 0L283.5 0ZM306.1 122L639 122 553.8 292.3C553.7 292.4 553.7 292.6 553.6 292.7L220.8 292.7 306.1 122ZM698.2 167.2L777.1 325C781.3 333.5 783.5 342.8 783.5 352.3L783.5 914.6C783.5 921.8 778.5 926.8 771.3 926.8L612.8 926.8 612.8 352.3C612.8 342.8 615 333.4 619.2 325L698.2 167.2ZM198.2 365.9L539.6 365.9 539.6 926.8 210.4 926.8C203.2 926.8 198.2 921.8 198.2 914.6L198.2 365.9ZM283.5 512.2C263.3 512.2 247 528.6 247 548.8 247 569 263.3 585.4 283.5 585.4 303.7 585.4 320.1 569 320.1 548.8 320.1 528.6 303.7 512.2 283.5 512.2L283.5 512.2ZM454.3 512.2C434.1 512.2 417.7 528.6 417.7 548.8 417.7 569 434.1 585.4 454.3 585.4 474.5 585.4 490.9 569 490.9 548.8 490.9 528.6 474.5 512.2 454.3 512.2ZM277.8 637C262.7 637.3 249.4 646.9 244.3 661.1 239.2 675.3 243.3 691.2 254.6 701.1 318.2 759.9 415.6 759.9 479.1 701.1 490.4 691 494.2 675 488.9 660.9 483.5 646.8 470.1 637.4 455 637.3 445.5 637.2 436.3 640.8 429.4 647.4 391.9 682.1 341.8 682.1 304.3 647.4 297.2 640.5 287.7 636.7 277.8 637Z", + "width": 1000 + }, + "search": [ + "milk" + ] + }, + { + "uid": "4a214d7548a54cfb7faae3dfa88e0146", + "css": "soda_happy", + "code": 59460, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M528.5 0L484.5 175.1 260.7 175.1C189.4 175.1 131.4 232.6 130.4 303.7L865.5 303.7C864.5 232.6 806.5 175.1 735.2 175.1L574.1 175.1 596.4 87 759.5 87 759.5 0 528.5 0ZM183.8 390.6L308.2 998.9 687.8 998.9 810.2 390.6 183.8 390.6ZM367.5 564.5C391.5 564.5 411 584 411 608 411 632 391.5 651.5 367.5 651.5 343.5 651.5 324 632 324 608 324 584 343.5 564.5 367.5 564.5ZM628.4 564.5C652.4 564.5 671.9 584 671.9 608 671.9 632 652.4 651.5 628.4 651.5 604.4 651.5 584.9 632 584.9 608 584.9 584 604.4 564.5 628.4 564.5ZM411 738.5L584.9 738.5C584.9 786.5 546 825.4 498 825.4 449.9 825.4 411 786.5 411 738.5Z", + "width": 1000 + }, + "search": [ + "soda_happy" + ] + }, + { + "uid": "204c319d1c414acb05ad3da52db53baa", + "css": "soda_unhappy", + "code": 59461, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M528.5 0L484.5 175.1 260.7 175.1C189.4 175.1 131.4 232.6 130.4 303.7L865.5 303.7C864.5 232.6 806.5 175.1 735.2 175.1L574.1 175.1 596.4 87 759.5 87 759.5 0 528.5 0ZM183.8 390.6L308.2 998.9 687.8 998.9 810.2 390.6 183.8 390.6ZM367.5 564.5C391.5 564.5 411 584 411 608 411 632 391.5 651.5 367.5 651.5 343.5 651.5 324 632 324 608 324 584 343.5 564.5 367.5 564.5ZM628.4 564.5C652.4 564.5 671.9 584 671.9 608 671.9 632 652.4 651.5 628.4 651.5 604.4 651.5 584.9 632 584.9 608 584.9 584 604.4 564.5 628.4 564.5ZM584.9 825.4L411 825.4C411 777.4 449.9 738.5 498 738.5 546 738.5 584.9 777.4 584.9 825.4Z", + "width": 1000 + }, + "search": [ + "soda_unhappy" + ] + }, + { + "uid": "7b10fc8ffcf02249a781b45e77849de4", + "css": "salt", + "code": 59449, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M729.4 402.6L669.6 318.1 669.6 272.7C684.1 267.5 694.6 253.8 694.6 237.5L694.6 162.5C694.6 72.9 621.7 0 532.1 0L457.1 0C367.5 0 294.6 72.9 294.6 162.5L294.6 237.5C294.6 253.8 305 267.5 319.6 272.7L319.6 317.2 279.9 368.2C176.5 501.4 119.6 667.5 119.6 836L119.6 862.5C119.6 938.3 181.3 1000 257.1 1000L732.1 1000C807.9 1000 869.6 938.3 869.6 862.5L869.6 843.2C869.6 684.5 821.1 532.1 729.4 402.6ZM557.1 50C577.8 50 594.6 66.8 594.6 87.5 594.6 108.2 577.8 125 557.1 125 536.4 125 519.6 108.2 519.6 87.5 519.6 66.8 536.4 50 557.1 50ZM432.1 50C452.8 50 469.6 66.8 469.6 87.5 469.6 108.2 452.8 125 432.1 125 411.4 125 394.6 108.2 394.6 87.5 394.6 66.8 411.4 50 432.1 50ZM514.9 850L470.5 850C432.9 850 400.2 830.4 387.3 811.1 377.8 796.7 381.6 777.3 396 767.8 410.2 758.3 429.4 762 439.1 776 441.9 779.2 454.2 787.5 470.6 787.5L514.9 787.5C531.3 787.5 544.6 774.9 544.6 759.4 544.6 743.9 531.3 731.3 514.9 731.3L480.5 731.3C429.7 731.3 388.3 690.6 388.3 640.6 388.3 590.6 429.7 550 480.5 550L513.3 550C563.6 550 587.3 585.6 589.8 589.7 599 604.3 594.5 623.6 579.9 632.8 565.3 641.9 546 637.5 536.9 622.8 536.5 622.4 529 612.5 513.4 612.5L480.5 612.5C464.2 612.5 450.8 625.1 450.8 640.6 450.8 656.1 464.1 668.8 480.5 668.8L514.9 668.8C565.7 668.8 607.1 709.4 607.1 759.4 607.1 809.4 565.7 850 514.9 850ZM350.2 400L386.7 353.1C391.8 346.5 394.6 338.4 394.6 330.1L394.6 275 594.6 275 594.6 330.1C594.6 337.8 597 345.4 601.4 351.7L635.6 400 350.2 400Z", + "width": 1000 + }, + "search": [ + "salt" + ] + }, + { + "uid": "6259d414372fbf5c12b19bf9ba7fd748", + "css": "magic_wand", + "code": 59395, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M648.1 0C637.1 0 626.1 5.5 619.9 16.5L569.2 105.9 470.3 78.4C445.9 71.7 423.5 94.1 430.3 118.4L457.8 217.4 368.3 268C346.4 280.5 346.4 312.1 368.3 324.5L457.8 375.1 458 375.4 588.1 245.2C618.8 214.5 668.5 214.5 699.2 245.2 729.9 275.8 729.9 325.6 699.2 356.3L569.1 486.5 569.3 486.7 619.9 576.1C632.3 598.1 664 598.1 676.4 576.1L727 486.7 826 514.1C850.4 520.8 872.7 498.5 866 474.2L838.5 375.1 927.9 324.6C949.9 312.1 949.9 280.5 927.9 268L838.5 217.4 866 118.4C872.8 94.1 850.4 71.7 826 78.4L727.1 105.9 676.4 16.5C670.2 5.5 659.2 0 648.1 0ZM457.9 375.4L41.5 791.8C10.9 822.5 10.9 872.3 41.5 902.9 72.2 933.6 122 933.6 152.6 902.9L569 486.5 457.9 375.4ZM610.5 666.2C590.1 666.5 573.8 683.3 574.1 703.7L574.1 740.7 537 740.7C523.7 740.6 511.3 747.6 504.5 759.1 497.8 770.6 497.8 784.9 504.5 796.4 511.3 808 523.7 815 537 814.8L574.1 814.8 574.1 851.9C573.9 865.2 580.9 877.6 592.4 884.4 604 891.1 618.2 891.1 629.8 884.4 641.3 877.6 648.3 865.2 648.1 851.9L648.1 814.8 685.2 814.8C698.5 815 711 808 717.7 796.4 724.4 784.9 724.4 770.6 717.7 759.1 711 747.6 698.5 740.6 685.2 740.7L648.1 740.7 648.1 703.7C648.3 693.7 644.4 684.1 637.3 677 630.2 669.9 620.5 666 610.5 666.2ZM869.8 777.3C849.4 777.6 833.1 794.4 833.3 814.8L833.3 851.9 796.3 851.9C782.9 851.7 770.5 858.7 763.8 870.2 757 881.8 757 896 763.8 907.6 770.5 919.1 782.9 926.1 796.3 925.9L833.3 925.9 833.3 963C833.1 976.3 840.2 988.7 851.7 995.5 863.2 1002.2 877.5 1002.2 889 995.5 900.6 988.7 907.6 976.3 907.4 963L907.4 925.9 944.4 925.9C957.8 926.1 970.2 919.1 977 907.6 983.7 896 983.7 881.8 977 870.2 970.2 858.7 957.8 851.7 944.4 851.9L907.4 851.9 907.4 814.8C907.5 804.8 903.6 795.2 896.5 788.1 889.4 781 879.8 777.1 869.8 777.3Z", + "width": 1000 + }, + "search": [ + "magic_wand" + ] + }, + { + "uid": "1c7d8102eb8ad70f78736c9f8c1feda8", + "css": "lab", + "code": 59402, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M277.8 0L277.8 111.1 333.3 111.1 333.3 388.9 24 820.7C9.2 839.6 0 863.1 0 888.9 0 950.3 49.7 1000 111.1 1000L888.9 1000C950.3 1000 1000 950.3 1000 888.9 1000 863.1 990.8 839.6 976 820.7L666.7 388.9 666.7 111.1 722.2 111.1 722.2 0 277.8 0ZM444.4 111.1L555.6 111.1 555.6 333.3 444.4 333.3 444.4 111.1ZM430.2 444.4L569.8 444.4 885.7 885.4 887.4 887 888.9 888.9 111.4 889.3 112.8 887.4 114.3 885.4 430.2 444.4ZM555.6 555.6C524.9 555.6 500 580.4 500 611.1 500 641.8 524.9 666.7 555.6 666.7 586.2 666.7 611.1 641.8 611.1 611.1 611.1 580.4 586.2 555.6 555.6 555.6ZM416.7 666.7C370.6 666.7 333.3 704 333.3 750 333.3 796 370.6 833.3 416.7 833.3 462.7 833.3 500 796 500 750 500 704 462.7 666.7 416.7 666.7Z", + "width": 1000 + }, + "search": [ + "lab" + ] + }, + { + "uid": "60645a65573e29e618b87e437ea2f75a", + "css": "sparkles", + "code": 59409, + "src": "custom_icons", + "selected": true, + "svg": { + "path": "M986 530L762 458 690 234C688 226 680 220 670 220 660 220 654 226 650 234L578 458 354 530C346 532 340 540 340 550 340 560 346 566 354 570L578 642 650 866C652 874 660 880 670 880 680 880 686 874 690 866L762 642 986 570C994 568 1000 560 1000 550 1000 540 994 534 986 530ZM34 278L190 330 242 486C244 494 252 500 260 500 268 500 276 494 278 486L330 330 486 278C494 276 500 268 500 260 500 252 494 244 486 242L330 190 278 34C276 26 268 20 260 20 252 20 244 26 242 34L190 190 34 242C26 244 20 252 20 260 20 268 26 276 34 278ZM346 780L236 744 200 634C196 626 188 620 180 620 172 620 164 626 162 634L126 744 14 780C6 784 0 792 0 800 0 808 6 816 14 818L124 854 160 964C164 974 172 980 180 980 188 980 196 974 198 966L234 856 344 820C352 818 358 810 358 802 358 794 354 784 346 780Z", + "width": 1000 + }, + "search": [ + "sparkles" + ] } ] } \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/fish.svg b/packages/smooth_app/assets/fonts/icons/fish.svg new file mode 100644 index 00000000000..8a4d4ce0d0b --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/fish.svg @@ -0,0 +1,7 @@ + + + Fish + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/icons.sketch b/packages/smooth_app/assets/fonts/icons/icons.sketch index fecc0788b9b..d87c126cdb2 100644 Binary files a/packages/smooth_app/assets/fonts/icons/icons.sketch and b/packages/smooth_app/assets/fonts/icons/icons.sketch differ diff --git a/packages/smooth_app/assets/fonts/icons/lab.svg b/packages/smooth_app/assets/fonts/icons/lab.svg new file mode 100644 index 00000000000..7f5e0d65912 --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/lab.svg @@ -0,0 +1,7 @@ + + + Laboratoy + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/magic_wand.svg b/packages/smooth_app/assets/fonts/icons/magic_wand.svg new file mode 100644 index 00000000000..c89cd3fb94d --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/magic_wand.svg @@ -0,0 +1,7 @@ + + + Magic Wand + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/milk.svg b/packages/smooth_app/assets/fonts/icons/milk.svg new file mode 100644 index 00000000000..1b7009773d9 --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/milk.svg @@ -0,0 +1,7 @@ + + + Milk + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/salt.svg b/packages/smooth_app/assets/fonts/icons/salt.svg new file mode 100644 index 00000000000..68dc808cef9 --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/salt.svg @@ -0,0 +1,9 @@ + + + Salt + + + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/soda_happy.svg b/packages/smooth_app/assets/fonts/icons/soda_happy.svg new file mode 100644 index 00000000000..58b7c93abee --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/soda_happy.svg @@ -0,0 +1,9 @@ + + + Soda happy + + + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/soda_unhappy.svg b/packages/smooth_app/assets/fonts/icons/soda_unhappy.svg new file mode 100644 index 00000000000..f54b6efde56 --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/soda_unhappy.svg @@ -0,0 +1,7 @@ + + + Soda unhappy + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/fonts/icons/sparkles.svg b/packages/smooth_app/assets/fonts/icons/sparkles.svg new file mode 100644 index 00000000000..6725e7d5cea --- /dev/null +++ b/packages/smooth_app/assets/fonts/icons/sparkles.svg @@ -0,0 +1,7 @@ + + + Sparkles + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/onboarding/birthday-cake.svg b/packages/smooth_app/assets/onboarding/birthday-cake.svg deleted file mode 100644 index a26a2c391ec..00000000000 --- a/packages/smooth_app/assets/onboarding/birthday-cake.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/smooth_app/assets/onboarding/cloud.svg b/packages/smooth_app/assets/onboarding/cloud.svg new file mode 100644 index 00000000000..8127531dc4e --- /dev/null +++ b/packages/smooth_app/assets/onboarding/cloud.svg @@ -0,0 +1,11 @@ + + + Vector + + + + + + + + \ No newline at end of file diff --git a/packages/smooth_app/assets/onboarding/reinvention.svg b/packages/smooth_app/assets/onboarding/reinvention.svg deleted file mode 100644 index 641f75117ca..00000000000 --- a/packages/smooth_app/assets/onboarding/reinvention.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/smooth_app/assets/onboarding/sample_product_data.json b/packages/smooth_app/assets/onboarding/sample_product_data.json index dfb0ce69e00..8da40ee7319 100644 --- a/packages/smooth_app/assets/onboarding/sample_product_data.json +++ b/packages/smooth_app/assets/onboarding/sample_product_data.json @@ -9,11 +9,11 @@ "description" : "", "description_short" : "Very good nutritional quality", "grade" : "a", - "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/nutriscore-a.svg", + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/nutriscore-a-new-en.svg", "id" : "nutriscore", - "match" : 85.0714285714286, + "match" : 81, "name" : "Nutri-Score", - "panel_id" : "nutriscore", + "panel_id" : "nutriscore_2023", "status" : "known", "title" : "Nutri-Score A" }, @@ -336,8 +336,6 @@ "Aliments d'origine végétale", "Aliments à base de fruits et de légumes", "Plats préparés", - "Alternatives à la viande", - "Substituts de viande", "Soupes", "Soupes de légumes" ], @@ -567,7 +565,6 @@ "ecoscore_material_score" : 81, "ecoscore_shape_ratio" : 1, "material" : "en:glass", - "number_of_units" : 1, "recycling" : "en:recycle", "shape" : "en:bottle" }, @@ -575,7 +572,6 @@ "ecoscore_material_score" : 76, "ecoscore_shape_ratio" : 0.1, "material" : "en:steel", - "number_of_units" : 1, "recycling" : "en:recycle", "shape" : "en:bottle-cap" } @@ -779,8 +775,6 @@ "ecoscore_grade" : "a", "ecoscore_score" : 115, "environment_impact_level_tags" : [], - "image_ingredients_url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_en.120.400.jpg", - "image_nutrition_url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_en.109.400.jpg", "ingredients_analysis_tags" : [ "en:palm-oil-free", "en:vegan", @@ -1183,7 +1177,7 @@ { "values" : [ { - "text" : "1 Bottle" + "text" : "Bottle" }, { "text" : "Glass" @@ -1201,7 +1195,7 @@ { "values" : [ { - "text" : "1 Bottle cap" + "text" : "Bottle cap" }, { "text" : "Steel" @@ -1283,7 +1277,7 @@ "title_element" : { "grade" : "a", "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/ecoscore-a.svg", - "subtitle" : "Product: A good product for you - Open Food Facts - 100 g", + "subtitle" : "Product: A good product for you - Open Food Facts - 200 g", "title" : "Impact for this product: A (Score: 115/100)", "type" : "grade" }, @@ -1373,37 +1367,9 @@ { "element_type" : "panel_group", "panel_group_element" : { - "image" : { - "alt" : "A good product for you - Nutrition facts", - "id" : "nutrition_en", - "lc" : "en", - "sizes" : { - "100" : { - "height" : 100, - "url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_en.109.100.jpg", - "width" : 75 - }, - "200" : { - "height" : 200, - "url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_en.109.200.jpg", - "width" : 150 - }, - "400" : { - "height" : 400, - "url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_en.109.400.jpg", - "width" : 300 - }, - "full" : { - "height" : 4032, - "url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_en.109.full.jpg", - "width" : 3024 - } - }, - "type" : "nutrition" - }, "panel_group_id" : "nutrition", "panel_ids" : [ - "nutriscore", + "nutriscore_2023", "nutrient_levels" ], "title" : "Nutrition", @@ -1422,34 +1388,6 @@ { "element_type" : "panel_group", "panel_group_element" : { - "image" : { - "alt" : "A good product for you - Ingredients", - "id" : "ingredients_en", - "lc" : "en", - "sizes" : { - "100" : { - "height" : 100, - "url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_en.120.100.jpg", - "width" : 56 - }, - "200" : { - "height" : 200, - "url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_en.120.200.jpg", - "width" : 113 - }, - "400" : { - "height" : 400, - "url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_en.120.400.jpg", - "width" : 225 - }, - "full" : { - "height" : 4624, - "url" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_en.120.full.jpg", - "width" : 2604 - } - }, - "type" : "ingredients" - }, "panel_group_id" : "ingredients", "panel_ids" : [ "ingredients" @@ -1492,7 +1430,7 @@ { "element_type" : "text", "text_element" : { - "html" : "\n If the information does not match the information on the packaging, please complete or correct it.\n Open Food Facts is a collaborative database, and every contribution is useful for all.\n " + "html" : "\n If the information does not match the information on the packaging, you can complete or correct it. Thank you!\n Open Food Facts is a collaborative database, and every contribution is useful for all.\n " } }, { @@ -1900,19 +1838,211 @@ "health" ] }, - "nutriscore" : { + "nutriscore_2023" : { + "elements" : [ + { + "element_type" : "panel", + "panel_element" : { + "panel_id" : "nutriscore_description" + } + }, + { + "element_type" : "panel_group", + "panel_group_element" : { + "evaluation" : "bad", + "icon_color_from_evaluation" : true, + "icon_size" : "small", + "icon_url" : "https://static.openfoodfacts.org/images/icons/dist/circle-minus.svg", + "panel_ids" : [ + "nutriscore_component_energy", + "nutriscore_component_sugars", + "nutriscore_component_saturated_fat", + "nutriscore_component_salt" + ], + "title" : "Negative points: 1/55" + } + }, + { + "element_type" : "panel_group", + "panel_group_element" : { + "evaluation" : "good", + "icon_color_from_evaluation" : true, + "icon_size" : "small", + "icon_url" : "https://static.openfoodfacts.org/images/icons/dist/circle-plus.svg", + "panel_ids" : [ + "nutriscore_component_proteins", + "nutriscore_component_fiber", + "nutriscore_component_fruits_vegetables_legumes" + ], + "title" : "Positive points: 2/17" + } + }, + { + "element_type" : "panel", + "panel_element" : { + "panel_id" : "nutriscore_details" + } + } + ], + "level" : "info", + "title_element" : { + "grade" : "a", + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/nutriscore-a-new-en.svg", + "subtitle" : "Very good nutritional quality", + "title" : "Nutri-Score A", + "type" : "grade" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_component_energy" : { "elements" : [ { "element_type" : "text", "text_element" : { - "html" : "Warning: the amount of fruits, vegetables and nuts is not specified on the label, it was estimated from the list of ingredients: 65\n ", - "type" : "warning" + "html" : "\n

\n \n Energy intakes above energy requirements are associated with increased risks of weight gain, overweight, obesity, and consequently risk of diet-related chronic diseases.\n

\n " } - }, + } + ], + "level" : "info", + "size" : "small", + "title_element" : { + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/points-negative-0-10.svg", + "subtitle" : "0/10 points (120kJ)", + "title" : "Calories" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_component_fiber" : { + "elements" : [ + { + "element_type" : "text", + "text_element" : { + "html" : "\n

\n Consuming foods rich in fiber (especially whole grain foods) reduces the risks of aerodigestive cancers, cardiovascular diseases, obesity and diabetes.\n \n

\n " + } + } + ], + "level" : "info", + "size" : "small", + "title_element" : { + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/points-positive-0-5.svg", + "subtitle" : "0/5 points (3g)", + "title" : "Fiber" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_component_fruits_vegetables_legumes" : { + "elements" : [ + { + "element_type" : "text", + "text_element" : { + "html" : "\n

\n Consuming foods rich in fruits, vegetables and legumes reduces the risks of aerodigestive cancers, cardiovascular diseases, obesity and diabetes.\n \n

\n " + } + } + ], + "level" : "info", + "size" : "small", + "title_element" : { + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/points-positive-2-5.svg", + "subtitle" : "2/5 points (65%)", + "title" : "Fruits, vegetables and legumes" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_component_proteins" : { + "elements" : [ + { + "element_type" : "text", + "text_element" : { + "html" : "\n

\n Foods that are rich in proteins are usually rich in calcium or iron which are essential minerals with numerous health benefits.\n \n

\n " + } + } + ], + "level" : "info", + "size" : "small", + "title_element" : { + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/points-positive-0-7.svg", + "subtitle" : "0/7 points (1g)", + "title" : "Proteins" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_component_salt" : { + "elements" : [ { "element_type" : "text", "text_element" : { - "html" : "\n\n

This product is not considered a beverage for the calculation of the Nutri-Score.

\n\n\n\n\n\n

\n Positive points: 5 \n

\n\n \n\n\n\n

\n Negative points: 1 \n

\n\n \n\n\n\n

The points for proteins are counted because the negative points are less than 11.

\n\n

\n Nutritional score: \n (1 - 5)\n

\n

Nutri-Score:

\n\n" + "html" : "\n

\n \n A high consumption of salt (or sodium) can cause raised blood pressure, which can increase the risk of heart disease and stroke.\n

\n " + } + } + ], + "level" : "info", + "size" : "small", + "title_element" : { + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/points-negative-1-20.svg", + "subtitle" : "1/20 points (0.4g)", + "title" : "Salt" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_component_saturated_fat" : { + "elements" : [ + { + "element_type" : "text", + "text_element" : { + "html" : "\n

\n \n A high consumption of fat, especially saturated fats, can raise cholesterol, which increases the risk of heart diseases.\n

\n " + } + } + ], + "level" : "info", + "size" : "small", + "title_element" : { + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/points-negative-0-10.svg", + "subtitle" : "0/10 points (0.2g)", + "title" : "Saturated fat" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_component_sugars" : { + "elements" : [ + { + "element_type" : "text", + "text_element" : { + "html" : "\n

\n \n A high consumption of sugar can cause weight gain and tooth decay. It also augments the risk of type 2 diabetes and cardio-vascular diseases.\n

\n " + } + } + ], + "level" : "info", + "size" : "small", + "title_element" : { + "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/points-negative-0-15.svg", + "subtitle" : "0/15 points (0.5g)", + "title" : "Sugar" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_description" : { + "elements" : [ + { + "element_type" : "text", + "text_element" : { + "html" : "

The Nutri-Score is a logo on the overall nutritional quality of products.

\n

\nThe score from A to E is calculated based on nutrients and foods to favor (proteins, fiber, fruits, vegetables and legumes ...) and nutrients to limit (calories, saturated fat, sugars, salt).\nThe score is calculated from the data of the nutrition facts table and the composition data (fruits, vegetables and legumes).\n

\n\n ", + "type" : "info" } }, { @@ -1924,10 +2054,37 @@ ], "level" : "info", "title_element" : { - "grade" : "a", - "icon_url" : "https://static.openfoodfacts.org/images/attributes/dist/nutriscore-a.svg", - "title" : "Very good nutritional quality", - "type" : "grade" + "icon_size" : "small", + "icon_url" : "https://static.openfoodfacts.org/images/icons/dist/info.svg", + "title" : "What is the Nutri-Score?", + "type" : "info" + }, + "topics" : [ + "health" + ] + }, + "nutriscore_details" : { + "elements" : [ + { + "element_type" : "text", + "text_element" : { + "html" : "Warning: the amount of fruits, vegetables and nuts is not specified on the label, it was estimated from the list of ingredients: 65\n ", + "type" : "warning" + } + }, + { + "element_type" : "text", + "text_element" : { + "html" : "\n\n

This product is not considered a beverage for the calculation of the Nutri-Score.

\n\n\n\n\n\n

Points for proteins are counted because the negative points are less than 11.

\n\n

\n Nutritional score: -1\n (1 - 2)\n

\n

Nutri-Score: A

\n " + } + } + ], + "level" : "info", + "title_element" : { + "icon_size" : "small", + "icon_url" : "https://static.openfoodfacts.org/images/icons/dist/info.svg", + "title" : "Details of the calculation of the Nutri-Score", + "type" : "info" }, "topics" : [ "health" @@ -1944,13 +2101,6 @@ "text" : "Nutrition facts", "type" : "text" }, - { - "column_group_id" : "product", - "shown_by_default" : true, - "text" : "As sold
for 100 g / 100 ml", - "text_for_small_screens" : "100g", - "type" : "text" - }, { "column_group_id" : "comparisons", "shown_by_default" : true, @@ -1967,9 +2117,6 @@ "style" : "max-width:15rem", "text" : "Energy" }, - { - "text" : "120 kj
(252 kcal)" - }, { "evaluation" : "good", "text" : "-54%" @@ -1983,12 +2130,9 @@ "style" : "max-width:15rem", "text" : "Fat" }, - { - "text" : "0.5 g" - }, { "evaluation" : "good", - "text" : "-69%" + "text" : "-68%" } ] }, @@ -1999,12 +2143,9 @@ "style" : "max-width:15rem", "text" : "Saturated fat" }, - { - "text" : "0.2 g" - }, { "evaluation" : "good", - "text" : "-45%" + "text" : "-42%" } ] }, @@ -2015,12 +2156,9 @@ "style" : "max-width:15rem", "text" : "Salt" }, - { - "text" : "0.4 g" - }, { "evaluation" : "good", - "text" : "-41%" + "text" : "-40%" } ] }, @@ -2031,9 +2169,6 @@ "style" : "max-width:15rem", "text" : "Carbohydrates" }, - { - "text" : "4 g" - }, { "text" : "-63%" } @@ -2046,12 +2181,9 @@ "style" : "max-width:15rem", "text" : "Fiber" }, - { - "text" : "3 g" - }, { "evaluation" : "good", - "text" : "+94%" + "text" : "+96%" } ] }, @@ -2062,12 +2194,9 @@ "style" : "max-width:15rem", "text" : "Sugars" }, - { - "text" : "0.5 g" - }, { "evaluation" : "good", - "text" : "-82%" + "text" : "-83%" } ] }, @@ -2078,9 +2207,6 @@ "style" : "max-width:15rem", "text" : "Proteins" }, - { - "text" : "1 g" - }, { "evaluation" : "bad", "text" : "-56%" @@ -2094,9 +2220,6 @@ "style" : "max-width:15rem", "text" : "Fruits‚ vegetables‚ nuts and rapeseed‚ walnut and olive oils (estimate from ingredients list analysis)" }, - { - "text" : "65 %" - }, { "text" : "" } @@ -2182,7 +2305,7 @@ "element_type" : "text", "text_element" : { "evaluation" : "good", - "html" : "\n \n \n 1 x \n \n Bottle\n \n \n \n (Glass)\n \n
\n \n \n \n 1 x \n \n Bottle cap\n \n \n \n (Steel)\n \n
\n \n \n ", + "html" : "\n \n \n \n \n Bottle\n \n \n \n (Glass)\n \n
\n \n \n \n \n \n Bottle cap\n \n \n \n (Steel)\n \n
\n \n \n ", "icon_alt" : "Recycle", "icon_color_from_evaluation" : true, "icon_url" : "https://static.openfoodfacts.org/images/icons/dist/recycle-variant.svg", @@ -2358,22 +2481,14 @@ } }, "labels_tags" : [ - "en:fair-trade", "en:organic", - "en:vegetarian", "en:eu-organic", - "en:vegan", - "en:green-dot", - "en:nutriscore" + "en:fair-trade" ], "labels_tags_fr" : [ - "Commerce équitable", "Bio", - "Végétarien", "Bio européen", - "Végétalien", - "Point Vert", - "Nutriscore" + "Commerce équitable" ], "lang" : "en", "nutrient_levels" : { @@ -2388,13 +2503,8 @@ "carbohydrates_unit" : "g", "carbohydrates_value" : 4, "carbon-footprint-from-known-ingredients_100g" : 18, - "carbon-footprint-from-known-ingredients_product" : 18, + "carbon-footprint-from-known-ingredients_product" : 36, "energy" : 120, - "energy-kcal" : 252, - "energy-kcal_100g" : 252, - "energy-kcal_unit" : "kcal", - "energy-kcal_value" : 252, - "energy-kcal_value_computed" : 30.5, "energy-kj" : 120, "energy-kj_100g" : 120, "energy-kj_unit" : "kJ", @@ -2443,44 +2553,8 @@ }, "nutrition_grade_fr" : "a", "product_name" : "A good product for you", - "product_quantity" : "100", - "quantity" : "100 g", - "selected_images" : { - "ingredients" : { - "display" : { - "en" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_en.120.400.jpg", - "es" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_es.113.400.jpg", - "fr" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_fr.123.400.jpg", - "it" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_it.140.400.jpg" - }, - "small" : { - "en" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_en.120.200.jpg", - "es" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_es.113.200.jpg", - "fr" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_fr.123.200.jpg", - "it" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_it.140.200.jpg" - }, - "thumb" : { - "en" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_en.120.100.jpg", - "es" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_es.113.100.jpg", - "fr" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_fr.123.100.jpg", - "it" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/ingredients_it.140.100.jpg" - } - }, - "nutrition" : { - "display" : { - "en" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_en.109.400.jpg", - "es" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_es.115.400.jpg" - }, - "small" : { - "en" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_en.109.200.jpg", - "es" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_es.115.200.jpg" - }, - "thumb" : { - "en" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_en.109.100.jpg", - "es" : "https://images.openfoodfacts.org/images/products/093/270/067/481501/nutrition_es.115.100.jpg" - } - } - }, + "product_quantity" : "200", + "quantity" : "200 g", "states_tags" : [ "en:to-be-completed", "en:nutrition-facts-completed", @@ -2494,12 +2568,7 @@ "en:packaging-to-be-completed", "en:quantity-completed", "en:product-name-completed", - "en:photos-to-be-validated", - "en:packaging-photo-to-be-selected", - "en:nutrition-photo-selected", - "en:ingredients-photo-selected", - "en:front-photo-to-be-selected", - "en:photos-uploaded" + "en:photos-to-be-uploaded" ] }, "status" : 1, diff --git a/packages/smooth_app/assets/onboarding/scan.svg b/packages/smooth_app/assets/onboarding/scan.svg deleted file mode 100644 index 0baf01438d4..00000000000 --- a/packages/smooth_app/assets/onboarding/scan.svg +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/smooth_app/ios/Gemfile.lock b/packages/smooth_app/ios/Gemfile.lock index d039a2eb59a..6c82f499c3b 100644 --- a/packages/smooth_app/ios/Gemfile.lock +++ b/packages/smooth_app/ios/Gemfile.lock @@ -16,17 +16,17 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.907.0) - aws-sdk-core (3.191.6) + aws-partitions (1.945.0) + aws-sdk-core (3.197.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.78.0) - aws-sdk-core (~> 3, >= 3.191.0) + aws-sdk-kms (1.85.0) + aws-sdk-core (~> 3, >= 3.197.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.146.1) - aws-sdk-core (~> 3, >= 3.191.0) + aws-sdk-s3 (1.152.3) + aws-sdk-core (~> 3, >= 3.197.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) aws-sigv4 (1.8.0) @@ -75,7 +75,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.220.0) + fastlane (2.221.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -156,31 +156,32 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.5) + http-cookie (1.0.6) domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) - jwt (2.8.1) + jwt (2.8.2) base64 - mini_magick (4.12.0) + mini_magick (4.13.1) mini_mime (1.1.5) multi_json (1.15.0) - multipart-post (2.4.0) + multipart-post (2.4.1) nanaimo (0.3.0) naturally (2.2.1) nkf (0.2.0) - optparse (0.4.0) + optparse (0.5.0) os (1.1.4) plist (3.7.1) - public_suffix (5.0.5) - rake (13.2.0) + public_suffix (5.1.1) + rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.6) + rexml (3.2.9) + strscan rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -193,6 +194,7 @@ GEM simctl (1.6.10) CFPropertyList naturally + strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) diff --git a/packages/smooth_app/ios/Podfile.lock b/packages/smooth_app/ios/Podfile.lock index 9043c25aa38..04989447e33 100644 --- a/packages/smooth_app/ios/Podfile.lock +++ b/packages/smooth_app/ios/Podfile.lock @@ -11,7 +11,7 @@ PODS: - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) - - flutter_custom_tabs (0.0.1): + - flutter_custom_tabs_ios (2.0.0): - Flutter - flutter_email_sender (0.0.1): - Flutter @@ -26,12 +26,9 @@ PODS: - Flutter - flutter_secure_storage (6.0.0): - Flutter - - FMDB (2.7.5): - - FMDB/standard (= 2.7.5) - - FMDB/standard (2.7.5) - - GoogleDataTransport (9.2.5): + - GoogleDataTransport (9.4.1): - GoogleUtilities/Environment (~> 7.7) - - nanopb (< 2.30910.0, >= 2.30908.0) + - nanopb (< 2.30911.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - GoogleMLKit/BarcodeScanning (4.0.0): - GoogleMLKit/MLKitCore @@ -50,12 +47,16 @@ PODS: - GoogleToolboxForMac/Defines (= 2.3.2) - "GoogleToolboxForMac/NSString+URLArguments (= 2.3.2)" - "GoogleToolboxForMac/NSString+URLArguments (2.3.2)" - - GoogleUtilities/Environment (7.12.0): + - GoogleUtilities/Environment (7.13.3): + - GoogleUtilities/Privacy - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.12.0): + - GoogleUtilities/Logger (7.13.3): - GoogleUtilities/Environment - - GoogleUtilities/UserDefaults (7.12.0): + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (7.13.3) + - GoogleUtilities/UserDefaults (7.13.3): - GoogleUtilities/Logger + - GoogleUtilities/Privacy - GoogleUtilitiesComponents (1.1.0): - GoogleUtilities/Logger - GTMSessionFetcher/Core (2.3.0) @@ -100,44 +101,42 @@ PODS: - GTMSessionFetcher/Core (< 3.0, >= 1.1) - MLImage (= 1.0.0-beta4) - MLKitCommon (~> 9.0) - - mobile_scanner (3.5.2): + - mobile_scanner (3.5.6): - Flutter - GoogleMLKit/BarcodeScanning (~> 4.0.0) - MTBBarcodeScanner (5.0.11) - - nanopb (2.30909.1): - - nanopb/decode (= 2.30909.1) - - nanopb/encode (= 2.30909.1) - - nanopb/decode (2.30909.1) - - nanopb/encode (2.30909.1) + - nanopb (2.30910.0): + - nanopb/decode (= 2.30910.0) + - nanopb/encode (= 2.30910.0) + - nanopb/decode (2.30910.0) + - nanopb/encode (2.30910.0) - package_info_plus (0.4.5): - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - permission_handler_apple (9.1.1): + - permission_handler_apple (9.3.0): - Flutter - - PromisesObjC (2.3.1) + - PromisesObjC (2.4.0) - qr_code_scanner (0.2.0): - Flutter - MTBBarcodeScanner - - ReachabilitySwift (5.0.0) + - ReachabilitySwift (5.2.2) - rive_common (0.0.1): - Flutter - - SDWebImage (5.18.5): - - SDWebImage/Core (= 5.18.5) - - SDWebImage/Core (5.18.5) - - SDWebImageWebPCoder (0.14.2): + - SDWebImage (5.19.2): + - SDWebImage/Core (= 5.19.2) + - SDWebImage/Core (5.19.2) + - SDWebImageWebPCoder (0.14.6): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.17) - - sensors_plus (0.0.1): - - Flutter - - Sentry/HybridSDK (8.15.2): - - SentryPrivate (= 8.15.2) + - Sentry/HybridSDK (8.21.0): + - SentryPrivate (= 8.21.0) - sentry_flutter (0.0.1): - Flutter - FlutterMacOS - - Sentry/HybridSDK (= 8.15.2) - - SentryPrivate (8.15.2) + - Sentry/HybridSDK (= 8.21.0) + - SentryPrivate (8.21.0) - share_plus (0.0.1): - Flutter - shared_preferences_foundation (0.0.1): @@ -145,7 +144,7 @@ PODS: - FlutterMacOS - sqflite (0.0.3): - Flutter - - FMDB (>= 2.7.5) + - FlutterMacOS - url_launcher_ios (0.0.1): - Flutter - webview_flutter_wkwebview (0.0.1): @@ -158,7 +157,7 @@ DEPENDENCIES: - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - - flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`) + - flutter_custom_tabs_ios (from `.symlinks/plugins/flutter_custom_tabs_ios/ios`) - flutter_email_sender (from `.symlinks/plugins/flutter_email_sender/ios`) - flutter_icmp_ping (from `.symlinks/plugins/flutter_icmp_ping/ios`) - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) @@ -174,17 +173,15 @@ DEPENDENCIES: - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`) - rive_common (from `.symlinks/plugins/rive_common/ios`) - - sensors_plus (from `.symlinks/plugins/sensors_plus/ios`) - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite (from `.symlinks/plugins/sqflite/ios`) + - sqflite (from `.symlinks/plugins/sqflite/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) SPEC REPOS: trunk: - - FMDB - GoogleDataTransport - GoogleMLKit - GoogleToolboxForMac @@ -219,8 +216,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter - flutter_custom_tabs: - :path: ".symlinks/plugins/flutter_custom_tabs/ios" + flutter_custom_tabs_ios: + :path: ".symlinks/plugins/flutter_custom_tabs_ios/ios" flutter_email_sender: :path: ".symlinks/plugins/flutter_email_sender/ios" flutter_icmp_ping: @@ -251,8 +248,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/qr_code_scanner/ios" rive_common: :path: ".symlinks/plugins/rive_common/ios" - sensors_plus: - :path: ".symlinks/plugins/sensors_plus/ios" sentry_flutter: :path: ".symlinks/plugins/sentry_flutter/ios" share_plus: @@ -260,7 +255,7 @@ EXTERNAL SOURCES: shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: - :path: ".symlinks/plugins/sqflite/ios" + :path: ".symlinks/plugins/sqflite/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" webview_flutter_wkwebview: @@ -269,25 +264,24 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: app_settings: 017320c6a680cdc94c799949d95b84cb69389ebc audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 - camera_avfoundation: 8b8d780bcfb6a4a02b0fbe2b4bd17b5b71946e68 + camera_avfoundation: 759172d1a77ae7be0de08fc104cfb79738b8a59e connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89 - flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b + flutter_custom_tabs_ios: a651b18786388923b62de8c0537607de87c2eccf + flutter_email_sender: 10a22605f92809a11ef52b2f412db806c6082d40 flutter_icmp_ping: 2b159955eee0c487c766ad83fec224ae35e7c935 flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e - flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef - flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be - FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - GoogleDataTransport: 54dee9d48d14580407f8f5fbf2f496e92437a2f2 + flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778 + flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 + GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleMLKit: 2bd0dc6253c4d4f227aad460f69215a504b2980e GoogleToolboxForMac: 8bef7c7c5cf7291c687cf5354f39f9db6399ad34 - GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 + GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2 - image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425 - in_app_review: 4a97249f7a2f539a0f294c2d9196b7fe35e49541 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d integration_test: 13825b8a9334a850581300559b8839134b124670 iso_countries: eb09d40f388e4c65e291e0bb36a701dfe7de6c74 libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 @@ -296,28 +290,27 @@ SPEC CHECKSUMS: MLKitBarcodeScanning: 04e264482c5f3810cb89ebc134ef6b61e67db505 MLKitCommon: c1b791c3e667091918d91bda4bba69a91011e390 MLKitVision: 8baa5f46ee3352614169b85250574fde38c36f49 - mobile_scanner: 5090a13b7a35fc1c25b0d97e18e84f271a6eb605 + mobile_scanner: 38dcd8a49d7d485f632b7de65e4900010187aef2 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb - nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 + nanopb: 438bc412db1928dac798aa6fd75726007be04262 package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c - permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 - PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e - ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - rive_common: 3a4c254c6e4db7e4b9e05daeb3d1f47ae4f7bf76 - SDWebImage: 7ac2b7ddc5e8484c79aa90fc4e30b149d6a2c88f - SDWebImageWebPCoder: 633b813fca24f1de5e076bcd7f720c038b23892b - sensors_plus: 4ee32bc7d61a055f27f88d3215ad6b6fb96a2b8e - Sentry: 6f5742b4c47c17c9adcf265f6f328cf4a0ed1923 - sentry_flutter: 2c309a1d4b45e59d02cfa15795705687f1e2081b - SentryPrivate: b2f7996f37781080f04a946eb4e377ff63c64195 + ReachabilitySwift: 2128f3a8c9107e1ad33574c6e58e8285d460b149 + rive_common: cbbac3192af00d7341f19dae2f26298e9e37d99e + SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a + SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 + Sentry: ebc12276bd17613a114ab359074096b6b3725203 + sentry_flutter: dff1df05dc39c83d04f9330b36360fc374574c5e + SentryPrivate: d651efb234cf385ec9a1cdd3eff94b5e78a0e0fe share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 - shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 - sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a - url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812 - webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36 PODFILE CHECKSUM: a2ded99d2ba03f677e98efb8fb92d57b3a65b96f -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/packages/smooth_app/ios/Runner.xcodeproj/project.pbxproj b/packages/smooth_app/ios/Runner.xcodeproj/project.pbxproj index 6352802e1ff..2d833e8484c 100644 --- a/packages/smooth_app/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/smooth_app/ios/Runner.xcodeproj/project.pbxproj @@ -266,6 +266,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 63B6828CA84AAF83AA1EFDEA /* [CP] Embed Pods Frameworks */, + 7B7420C49BA891584B8660ED /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -282,7 +283,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -483,6 +484,23 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 7B7420C49BA891584B8660ED /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -603,11 +621,9 @@ 3B866D912A81BF8800CEE82C /* yi */, 3B866D922A81BF8800CEE82C /* xh */, 3B866D932A81BF8900CEE82C /* wo */, - 3B866D942A81BF8900CEE82C /* vls */, 3B866D952A81BF8A00CEE82C /* cy */, 3B866D962A81BF8A00CEE82C /* wa */, 3B866D972A81BF8B00CEE82C /* vi */, - 3B866D982A81BF8C00CEE82C /* vec */, 3B866D992A81BF8C00CEE82C /* ve */, 3B866D9B2A81BF8D00CEE82C /* uz */, 3B866D9C2A81BF8E00CEE82C /* ug */, diff --git a/packages/smooth_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/smooth_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a6b826db27d..5e31d3d342f 100644 --- a/packages/smooth_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/smooth_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ ProductQuery.uriProductHelper; + + /// Returns true if tasks with the same stamp would overwrite each-other. + bool isDeduplicable() => true; } diff --git a/packages/smooth_app/lib/background/background_task_add_price.dart b/packages/smooth_app/lib/background/background_task_add_price.dart new file mode 100644 index 00000000000..bff36bc9f2c --- /dev/null +++ b/packages/smooth_app/lib/background/background_task_add_price.dart @@ -0,0 +1,416 @@ +import 'dart:async'; + +import 'package:crop_image/crop_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:http_parser/http_parser.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/background/background_task.dart'; +import 'package:smooth_app/background/background_task_image.dart'; +import 'package:smooth_app/background/background_task_upload.dart'; +import 'package:smooth_app/background/operation_type.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; +import 'package:smooth_app/pages/prices/eraser_model.dart'; +import 'package:smooth_app/pages/prices/eraser_painter.dart'; + +// TODO(monsieurtanuki): use transient file, in order to have instant access to proof image? +/// Background task about adding a product price. +class BackgroundTaskAddPrice extends BackgroundTask { + BackgroundTaskAddPrice._({ + required super.processName, + required super.uniqueId, + required super.stamp, + // single + required this.fullPath, + required this.rotationDegrees, + required this.cropX1, + required this.cropY1, + required this.cropX2, + required this.cropY2, + required this.proofType, + required this.date, + required this.currency, + required this.locationOSMId, + required this.locationOSMType, + // lines + required this.eraserCoordinates, + // multi + required this.barcodes, + required this.pricesAreDiscounted, + required this.prices, + required this.pricesWithoutDiscount, + }); + + BackgroundTaskAddPrice.fromJson(Map json) + : fullPath = json[_jsonTagImagePath] as String, + rotationDegrees = json[_jsonTagRotation] as int? ?? 0, + cropX1 = json[_jsonTagX1] as int? ?? 0, + cropY1 = json[_jsonTagY1] as int? ?? 0, + cropX2 = json[_jsonTagX2] as int? ?? 0, + cropY2 = json[_jsonTagY2] as int? ?? 0, + proofType = ProofType.fromOffTag(json[_jsonTagProofType] as String)!, + date = JsonHelper.stringTimestampToDate(json[_jsonTagDate] as String), + currency = Currency.fromName(json[_jsonTagCurrency] as String)!, + locationOSMId = json[_jsonTagOSMId] as int, + locationOSMType = + LocationOSMType.fromOffTag(json[_jsonTagOSMType] as String)!, + eraserCoordinates = + _fromJsonListDouble(json[_jsonTagEraserCoordinates]), + barcodes = json.containsKey(_jsonTagBarcode) + ? [json[_jsonTagBarcode] as String] + : _fromJsonListString(json[_jsonTagBarcodes])!, + pricesAreDiscounted = json.containsKey(_jsonTagIsDiscounted) + ? [json[_jsonTagIsDiscounted] as bool] + : _fromJsonListBool(json[_jsonTagAreDiscounted])!, + prices = json.containsKey(_jsonTagPrice) + ? [json[_jsonTagPrice] as double] + : _fromJsonListDouble(json[_jsonTagPrices])!, + pricesWithoutDiscount = json.containsKey(_jsonTagPriceWithoutDiscount) + ? [json[_jsonTagPriceWithoutDiscount] as double?] + : _fromJsonListNullableDouble(json[_jsonTagPricesWithoutDiscount])!, + super.fromJson(json); + + static List? _fromJsonListDouble(final List? input) { + if (input == null) { + return null; + } + final List result = []; + for (final dynamic item in input) { + result.add(item as double); + } + return result; + } + + static List? _fromJsonListNullableDouble( + final List? input, + ) { + if (input == null) { + return null; + } + final List result = []; + for (final dynamic item in input) { + result.add(item as double?); + } + return result; + } + + static List? _fromJsonListString(final List? input) { + if (input == null) { + return null; + } + final List result = []; + for (final dynamic item in input) { + result.add(item as String); + } + return result; + } + + static List? _fromJsonListBool(final List? input) { + if (input == null) { + return null; + } + final List result = []; + for (final dynamic item in input) { + result.add(item as bool); + } + return result; + } + + static const String _jsonTagImagePath = 'imagePath'; + static const String _jsonTagRotation = 'rotation'; + static const String _jsonTagX1 = 'x1'; + static const String _jsonTagY1 = 'y1'; + static const String _jsonTagX2 = 'x2'; + static const String _jsonTagY2 = 'y2'; + static const String _jsonTagProofType = 'proofType'; + static const String _jsonTagDate = 'date'; + static const String _jsonTagEraserCoordinates = 'eraserCoordinates'; + static const String _jsonTagCurrency = 'currency'; + static const String _jsonTagOSMId = 'osmId'; + static const String _jsonTagOSMType = 'osmType'; + static const String _jsonTagBarcodes = 'barcodes'; + static const String _jsonTagAreDiscounted = 'areDiscounted'; + static const String _jsonTagPrices = 'prices'; + static const String _jsonTagPricesWithoutDiscount = 'pricesWithoutDiscount'; + @Deprecated('Use [_jsonTagBarcodes] instead') + static const String _jsonTagBarcode = 'barcode'; + @Deprecated('Use [_jsonTagAreDiscounted] instead') + static const String _jsonTagIsDiscounted = 'isDiscounted'; + @Deprecated('Use [_jsonTagPrices] instead') + static const String _jsonTagPrice = 'price'; + @Deprecated('Use [_jsonTagPricesWithoutDiscount] instead') + static const String _jsonTagPriceWithoutDiscount = 'priceWithoutDiscount'; + + static const OperationType _operationType = OperationType.addPrice; + + final String fullPath; + final int rotationDegrees; + final int cropX1; + final int cropY1; + final int cropX2; + final int cropY2; + final ProofType proofType; + final DateTime date; + final Currency currency; + final int locationOSMId; + final LocationOSMType locationOSMType; + final List? eraserCoordinates; + // per line + final List barcodes; + final List pricesAreDiscounted; + final List prices; + final List pricesWithoutDiscount; + + @override + Map toJson() { + final Map result = super.toJson(); + result[_jsonTagImagePath] = fullPath; + result[_jsonTagRotation] = rotationDegrees; + result[_jsonTagX1] = cropX1; + result[_jsonTagY1] = cropY1; + result[_jsonTagX2] = cropX2; + result[_jsonTagY2] = cropY2; + result[_jsonTagProofType] = proofType.offTag; + result[_jsonTagDate] = date.toIso8601String(); + result[_jsonTagCurrency] = currency.name; + result[_jsonTagOSMId] = locationOSMId; + result[_jsonTagOSMType] = locationOSMType.offTag; + result[_jsonTagEraserCoordinates] = eraserCoordinates; + result[_jsonTagBarcodes] = barcodes; + result[_jsonTagAreDiscounted] = pricesAreDiscounted; + result[_jsonTagPrices] = prices; + result[_jsonTagPricesWithoutDiscount] = pricesWithoutDiscount; + return result; + } + + /// Adds the background task about uploading a product image. + static Future addTask({ + required final BuildContext context, + required final CropParameters cropObject, + required final ProofType proofType, + required final DateTime date, + required final Currency currency, + required final int locationOSMId, + required final LocationOSMType locationOSMType, + required final List barcodes, + required final List pricesAreDiscounted, + required final List prices, + required final List pricesWithoutDiscount, + }) async { + final LocalDatabase localDatabase = context.read(); + final String uniqueId = await _operationType.getNewKey(localDatabase); + final BackgroundTask task = _getNewTask( + uniqueId: uniqueId, + cropObject: cropObject, + proofType: proofType, + date: date, + currency: currency, + locationOSMId: locationOSMId, + locationOSMType: locationOSMType, + barcodes: barcodes, + pricesAreDiscounted: pricesAreDiscounted, + prices: prices, + pricesWithoutDiscount: pricesWithoutDiscount, + ); + if (!context.mounted) { + return; + } + await task.addToManager(localDatabase, context: context); + } + + @override + (String, AlignmentGeometry)? getFloatingMessage( + final AppLocalizations appLocalizations) => + ( + appLocalizations.add_price_queued, + AlignmentDirectional.center, + ); + + /// Returns a new background task about changing a product. + static BackgroundTaskAddPrice _getNewTask({ + required final String uniqueId, + required final CropParameters cropObject, + required final ProofType proofType, + required final DateTime date, + required final Currency currency, + required final int locationOSMId, + required final LocationOSMType locationOSMType, + required final List barcodes, + required final List pricesAreDiscounted, + required final List prices, + required final List pricesWithoutDiscount, + }) => + BackgroundTaskAddPrice._( + uniqueId: uniqueId, + processName: _operationType.processName, + fullPath: cropObject.fullFile!.path, + rotationDegrees: cropObject.rotation, + cropX1: cropObject.x1, + cropY1: cropObject.y1, + cropX2: cropObject.x2, + cropY2: cropObject.y2, + proofType: proofType, + date: date, + currency: currency, + locationOSMId: locationOSMId, + locationOSMType: locationOSMType, + eraserCoordinates: cropObject.eraserCoordinates, + barcodes: barcodes, + pricesAreDiscounted: pricesAreDiscounted, + prices: prices, + pricesWithoutDiscount: pricesWithoutDiscount, + stamp: _getStamp( + date: date, + locationOSMId: locationOSMId, + locationOSMType: locationOSMType, + ), + ); + + static String _getStamp({ + required final DateTime date, + required final int locationOSMId, + required final LocationOSMType locationOSMType, + }) => + 'no_barcode;price;$date;$locationOSMId;$locationOSMType'; + + @override + Future postExecute( + final LocalDatabase localDatabase, + final bool success, + ) async { + await super.postExecute(localDatabase, success); + try { + (await BackgroundTaskUpload.getFile(fullPath)).deleteSync(); + } catch (e) { + // not likely, but let's not spoil the task for that either. + } + try { + (await BackgroundTaskUpload.getFile( + BackgroundTaskImage.getCroppedPath(fullPath))) + .deleteSync(); + } catch (e) { + // possible, but let's not spoil the task for that either. + } + } + + @override + Future preExecute(final LocalDatabase localDatabase) async {} + + @override + Future execute(final LocalDatabase localDatabase) async { + final List offsets = []; + if (eraserCoordinates != null) { + for (int i = 0; i < eraserCoordinates!.length; i += 2) { + final Offset offset = Offset( + eraserCoordinates![i], + eraserCoordinates![i + 1], + ); + offsets.add(offset); + } + } + final String? path = await BackgroundTaskImage.cropIfNeeded( + fullPath: fullPath, + croppedPath: BackgroundTaskImage.getCroppedPath(fullPath), + rotationDegrees: rotationDegrees, + cropX1: cropX1, + cropY1: cropY1, + cropX2: cropX2, + cropY2: cropY2, + overlayPainter: offsets.isEmpty + ? null + : EraserPainter( + eraserModel: EraserModel( + rotation: CropRotationExtension.fromDegrees(rotationDegrees)!, + offsets: offsets, + ), + cropRect: BackgroundTaskImage.getDownsizedRect( + cropX1, + cropY1, + cropX2, + cropY2, + ), + ), + ); + if (path == null) { + // TODO(monsieurtanuki): maybe something more refined when we dismiss the picture, like alerting the user, though it's not supposed to happen anymore from upstream. + return; + } + + // authentication + final User user = getUser(); + final MaybeError token = + await OpenPricesAPIClient.getAuthenticationToken( + username: user.userId, + password: user.password, + uriHelper: uriProductHelper, + ); + if (token.isError) { + throw Exception('Could not get token: ${token.error}'); + } + if (token.value.isEmpty) { + throw Exception('Unexpected empty token'); + } + final String bearerToken = token.value; + + // proof upload + final Uri initialImageUri = Uri.parse(path); + final MediaType initialMediaType = + HttpHelper().imagineMediaType(initialImageUri.path)!; + final MaybeError uploadProof = await OpenPricesAPIClient.uploadProof( + proofType: proofType, + imageUri: initialImageUri, + mediaType: initialMediaType, + bearerToken: bearerToken, + uriHelper: uriProductHelper, + ); + if (uploadProof.isError) { + throw Exception('Could not upload proof: ${uploadProof.error}'); + } + + for (int i = 0; i < barcodes.length; i++) { + final Price newPrice = Price() + ..date = date + ..currency = currency + ..locationOSMId = locationOSMId + ..locationOSMType = locationOSMType + ..proofId = uploadProof.value.id + ..priceIsDiscounted = pricesAreDiscounted[i] + ..price = prices[i] + ..priceWithoutDiscount = pricesWithoutDiscount[i] + ..productCode = barcodes[i]; + + // create price + final MaybeError addedPrice = + await OpenPricesAPIClient.createPrice( + price: newPrice, + bearerToken: bearerToken, + uriHelper: uriProductHelper, + ); + if (addedPrice.isError) { + throw Exception('Could not add price: ${addedPrice.error}'); + } + } + + // close session + final MaybeError closedSession = + await OpenPricesAPIClient.deleteUserSession( + uriHelper: uriProductHelper, + bearerToken: bearerToken, + ); + if (closedSession.isError) { + // TODO(monsieurtanuki): do we really care? + // throw Exception('Could not close session: ${closedSession.error}'); + return; + } + if (!closedSession.value) { + // TODO(monsieurtanuki): do we really care? + // throw Exception('Could not really close session'); + return; + } + } + + @override + bool isDeduplicable() => false; +} diff --git a/packages/smooth_app/lib/background/background_task_crop.dart b/packages/smooth_app/lib/background/background_task_crop.dart index b457d6e3610..9e7a4e30888 100644 --- a/packages/smooth_app/lib/background/background_task_crop.dart +++ b/packages/smooth_app/lib/background/background_task_crop.dart @@ -149,7 +149,7 @@ class BackgroundTaskCrop extends BackgroundTaskUpload { ) async { await super.postExecute(localDatabase, success); try { - (await getFile(croppedPath)).deleteSync(); + (await BackgroundTaskUpload.getFile(croppedPath)).deleteSync(); } catch (e) { // not likely, but let's not spoil the task for that either. } diff --git a/packages/smooth_app/lib/background/background_task_image.dart b/packages/smooth_app/lib/background/background_task_image.dart index b15f9871cd0..be4bcb5b207 100644 --- a/packages/smooth_app/lib/background/background_task_image.dart +++ b/packages/smooth_app/lib/background/background_task_image.dart @@ -135,12 +135,6 @@ class BackgroundTaskImage extends BackgroundTaskUpload { ), ); - /// Returns true if the stamp is an "image/OTHER" stamp. - /// - /// That's important because "image/OTHER" task are never duplicates. - static bool isOtherStamp(final String stamp) => - stamp.contains(';image;${ImageField.OTHER.offTag};'); - /// Returns a fake value that means: "remove the previous value when merging". /// /// If we use this task, it means that we took a brand new picture. Therefore, @@ -162,17 +156,18 @@ class BackgroundTaskImage extends BackgroundTaskUpload { ) async { await super.postExecute(localDatabase, success); try { - (await getFile(fullPath)).deleteSync(); + (await BackgroundTaskUpload.getFile(fullPath)).deleteSync(); } catch (e) { // not likely, but let's not spoil the task for that either. } try { - (await getFile(croppedPath)).deleteSync(); + (await BackgroundTaskUpload.getFile(croppedPath)).deleteSync(); } catch (e) { // not likely, but let's not spoil the task for that either. } try { - (await getFile(_getCroppedPath())).deleteSync(); + (await BackgroundTaskUpload.getFile(getCroppedPath(fullPath))) + .deleteSync(); } catch (e) { // possible, but let's not spoil the task for that either. } @@ -204,38 +199,61 @@ class BackgroundTaskImage extends BackgroundTaskUpload { source.bottom * factor, ); + static Rect getUpsizedRect(final Rect source) => + getResizedRect(source, _cropConversionFactor); + + static Rect getDownsizedRect( + final int cropX1, + final int cropY1, + final int cropX2, + final int cropY2, + ) => + getResizedRect( + Rect.fromLTRB( + cropX1.toDouble(), + cropY1.toDouble(), + cropX2.toDouble(), + cropY2.toDouble(), + ), + 1 / _cropConversionFactor, + ); + /// Conversion factor to `int` from / to UI / background task. - static const int cropConversionFactor = 1000000; + static const int _cropConversionFactor = 1000000; - /// Returns true if a crop operation is needed - after having performed it. + /// Returns the file path of a crop operation. /// - /// Returns false if no crop operation is needed. + /// Returns directly the original [fullPath] if no crop operation was needed. + /// Returns the path of the cropped file if relevant. /// Returns null if the image (cropped or not) is too small. - Future _crop(final File file) async { - final ui.Image full = - await loadUiImage(await (await getFile(fullPath)).readAsBytes()); + static Future cropIfNeeded({ + required final String fullPath, + required final String croppedPath, + required final int rotationDegrees, + required final int cropX1, + required final int cropY1, + required final int cropX2, + required final int cropY2, + final CustomPainter? overlayPainter, + }) async { + final ui.Image full = await loadUiImage( + await (await BackgroundTaskUpload.getFile(fullPath)).readAsBytes()); if (cropX1 == 0 && cropY1 == 0 && - cropX2 == cropConversionFactor && - cropY2 == cropConversionFactor && + cropX2 == _cropConversionFactor && + cropY2 == _cropConversionFactor && rotationDegrees == 0) { if (!isPictureBigEnough(full.width, full.height)) { return null; } // in that case, no need to crop - return false; + if (overlayPainter == null) { + return fullPath; + } } Size getCroppedSize() { - final Rect cropRect = getResizedRect( - Rect.fromLTRB( - cropX1.toDouble(), - cropY1.toDouble(), - cropX2.toDouble(), - cropY2.toDouble(), - ), - 1 / cropConversionFactor, - ); + final Rect cropRect = getDownsizedRect(cropX1, cropY1, cropX2, cropY2); switch (CropRotationExtension.fromDegrees(rotationDegrees)!) { case CropRotation.up: case CropRotation.down: @@ -257,43 +275,39 @@ class BackgroundTaskImage extends BackgroundTaskUpload { return null; } final ui.Image cropped = await CropController.getCroppedBitmap( - crop: getResizedRect( - Rect.fromLTRB( - cropX1.toDouble(), - cropY1.toDouble(), - cropX2.toDouble(), - cropY2.toDouble(), - ), - 1 / cropConversionFactor, - ), + crop: getDownsizedRect(cropX1, cropY1, cropX2, cropY2), rotation: CropRotationExtension.fromDegrees(rotationDegrees)!, image: full, maxSize: null, quality: FilterQuality.high, + overlayPainter: overlayPainter, ); - await saveJpeg(file: file, source: cropped); - return true; + await saveJpeg( + file: await BackgroundTaskUpload.getFile(croppedPath), + source: cropped, + ); + return croppedPath; } - /// Returns the path of the locally computed cropped path (if relevant). - String _getCroppedPath() => '$fullPath.cropped.jpg'; + static String getCroppedPath(final String fullPath) => + '$fullPath.cropped.jpg'; /// Uploads the product image. @override Future upload() async { - final String path; - final String croppedPath = _getCroppedPath(); - final bool? neededCrop = await _crop(await getFile(croppedPath)); - if (neededCrop == null) { + final String? path = await cropIfNeeded( + fullPath: fullPath, + croppedPath: getCroppedPath(fullPath), + rotationDegrees: rotationDegrees, + cropX1: cropX1, + cropY1: cropY1, + cropX2: cropX2, + cropY2: cropY2, + ); + if (path == null) { // TODO(monsieurtanuki): maybe something more refined when we dismiss the picture, like alerting the user, though it's not supposed to happen anymore from upstream. return; } - if (neededCrop) { - path = croppedPath; - } else { - path = fullPath; - } - final ImageField imageField = ImageField.fromOffTag(this.imageField)!; final OpenFoodFactsLanguage language = getLanguage(); final User user = getUser(); diff --git a/packages/smooth_app/lib/background/background_task_manager.dart b/packages/smooth_app/lib/background/background_task_manager.dart index 0fa9d5694a9..66704385daf 100644 --- a/packages/smooth_app/lib/background/background_task_manager.dart +++ b/packages/smooth_app/lib/background/background_task_manager.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:flutter/rendering.dart'; import 'package:smooth_app/background/background_task.dart'; -import 'package:smooth_app/background/background_task_image.dart'; import 'package:smooth_app/background/background_task_refresh_later.dart'; import 'package:smooth_app/background/operation_type.dart'; import 'package:smooth_app/data_models/login_result.dart'; @@ -275,8 +274,7 @@ class BackgroundTaskManager { // now let's get rid of stamp duplicates. final String stamp = task.stamp; _debugPrint('task $taskId, stamp: $stamp'); - // for image/OTHER we don't remove duplicates (they are NOT duplicates) - if (!BackgroundTaskImage.isOtherStamp(stamp)) { + if (task.isDeduplicable()) { int? removeMe; for (int i = 0; i < result.length; i++) { // it's the same stamp, we can remove the previous task. @@ -293,7 +291,7 @@ class BackgroundTaskManager { result.removeAt(removeMe); } } else { - _debugPrint('is "other" stamp!'); + _debugPrint('is "not deduplicable" task!'); } result.add(task); } diff --git a/packages/smooth_app/lib/background/background_task_unselect.dart b/packages/smooth_app/lib/background/background_task_unselect.dart index 49242e72989..f952babbdd5 100644 --- a/packages/smooth_app/lib/background/background_task_unselect.dart +++ b/packages/smooth_app/lib/background/background_task_unselect.dart @@ -17,6 +17,7 @@ class BackgroundTaskUnselect extends BackgroundTaskBarcode BackgroundTaskUnselect._({ required super.processName, required super.uniqueId, + required OpenFoodFactsLanguage super.language, required super.barcode, required super.stamp, required this.imageField, @@ -81,6 +82,7 @@ class BackgroundTaskUnselect extends BackgroundTaskBarcode BackgroundTaskUnselect._( uniqueId: uniqueId, barcode: barcode, + language: language, processName: _operationType.processName, imageField: imageField.offTag, // same stamp as image upload diff --git a/packages/smooth_app/lib/background/background_task_upload.dart b/packages/smooth_app/lib/background/background_task_upload.dart index 766e43088a9..7d069f0f71c 100644 --- a/packages/smooth_app/lib/background/background_task_upload.dart +++ b/packages/smooth_app/lib/background/background_task_upload.dart @@ -108,8 +108,7 @@ abstract class BackgroundTaskUpload extends BackgroundTaskBarcode /// we use in [getDirectory]. /// With this method we refresh the path for iOS. /// cf. https://github.com/openfoodfacts/smooth-app/issues/4725 - @protected - Future getFile(String path) async { + static Future getFile(String path) async { if (Platform.isIOS) { final int lastSeparator = path.lastIndexOf('/'); final String filename = @@ -139,4 +138,11 @@ abstract class BackgroundTaskUpload extends BackgroundTaskBarcode /// /// cf. [UpToDateChanges._overwrite] regarding `images` field. ProductImage getProductImageChange(); + + /// Returns true only if it's not a "image/OTHER" task. + /// + /// That's important because "image/OTHER" task are never duplicates. + @override + bool isDeduplicable() => + !stamp.contains(';image;${ImageField.OTHER.offTag};'); } diff --git a/packages/smooth_app/lib/background/operation_type.dart b/packages/smooth_app/lib/background/operation_type.dart index 869af3588d0..92cee088a82 100644 --- a/packages/smooth_app/lib/background/operation_type.dart +++ b/packages/smooth_app/lib/background/operation_type.dart @@ -1,5 +1,6 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:smooth_app/background/background_task.dart'; +import 'package:smooth_app/background/background_task_add_price.dart'; import 'package:smooth_app/background/background_task_crop.dart'; import 'package:smooth_app/background/background_task_details.dart'; import 'package:smooth_app/background/background_task_download_products.dart'; @@ -35,6 +36,7 @@ enum OperationType { offlineProducts('P', 'OFFLINE_PRODUCTS'), fullRefresh('F', 'FULL_REFRESH'), languageRefresh('L', 'LANGUAGE_REFRESH'), + addPrice('A', 'ADD_PRICE'), details('D', 'PRODUCT_EDIT'); const OperationType(this.header, this.processName); @@ -65,62 +67,41 @@ enum OperationType { '$_transientHeaderSeparator${work ?? ''}'; } - BackgroundTask fromJson(Map map) { - switch (this) { - case crop: - return BackgroundTaskCrop.fromJson(map); - case details: - return BackgroundTaskDetails.fromJson(map); - case hungerGames: - return BackgroundTaskHungerGames.fromJson(map); - case image: - return BackgroundTaskImage.fromJson(map); - case refreshLater: - return BackgroundTaskRefreshLater.fromJson(map); - case unselect: - return BackgroundTaskUnselect.fromJson(map); - case offline: - return BackgroundTaskOffline.fromJson(map); - case offlineBarcodes: - return BackgroundTaskTopBarcodes.fromJson(map); - case offlineProducts: - return BackgroundTaskDownloadProducts.fromJson(map); - case fullRefresh: - return BackgroundTaskFullRefresh.fromJson(map); - case languageRefresh: - return BackgroundTaskLanguageRefresh.fromJson(map); - } - } + BackgroundTask fromJson(Map map) => switch (this) { + crop => BackgroundTaskCrop.fromJson(map), + addPrice => BackgroundTaskAddPrice.fromJson(map), + details => BackgroundTaskDetails.fromJson(map), + hungerGames => BackgroundTaskHungerGames.fromJson(map), + image => BackgroundTaskImage.fromJson(map), + refreshLater => BackgroundTaskRefreshLater.fromJson(map), + unselect => BackgroundTaskUnselect.fromJson(map), + offline => BackgroundTaskOffline.fromJson(map), + offlineBarcodes => BackgroundTaskTopBarcodes.fromJson(map), + offlineProducts => BackgroundTaskDownloadProducts.fromJson(map), + fullRefresh => BackgroundTaskFullRefresh.fromJson(map), + languageRefresh => BackgroundTaskLanguageRefresh.fromJson(map), + }; bool matches(final TransientOperation action) => action.key.startsWith('$header$_transientHeaderSeparator'); - String getLabel(final AppLocalizations appLocalizations) { - switch (this) { - case OperationType.details: - return appLocalizations.background_task_operation_details; - case OperationType.image: - return appLocalizations.background_task_operation_image; - case OperationType.unselect: - return 'Unselect a product image'; - case OperationType.hungerGames: - return 'Answering to a Hunger Games question'; - case OperationType.crop: - return 'Crop an existing image'; - case OperationType.refreshLater: - return 'Waiting 10 min before refreshing product to get all automatic edits'; - case OperationType.offline: - return 'Downloading top n products for offline usage'; - case OperationType.offlineBarcodes: - return 'Downloading top n barcodes'; - case OperationType.offlineProducts: - return 'Downloading products'; - case OperationType.fullRefresh: - return 'Refreshing the full local database'; - case OperationType.languageRefresh: - return 'Refreshing the local database to a new language'; - } - } + String getLabel(final AppLocalizations appLocalizations) => switch (this) { + OperationType.details => + appLocalizations.background_task_operation_details, + OperationType.addPrice => 'Add price', + OperationType.image => appLocalizations.background_task_operation_image, + OperationType.unselect => 'Unselect a product image', + OperationType.hungerGames => 'Answering to a Hunger Games question', + OperationType.crop => 'Crop an existing image', + OperationType.refreshLater => + 'Waiting 10 min before refreshing product to get all automatic edits', + OperationType.offline => 'Downloading top n products for offline usage', + OperationType.offlineBarcodes => 'Downloading top n barcodes', + OperationType.offlineProducts => 'Downloading products', + OperationType.fullRefresh => 'Refreshing the full local database', + OperationType.languageRefresh => + 'Refreshing the local database to a new language', + }; static int getSequentialId(final TransientOperation operation) { final List keyItems = diff --git a/packages/smooth_app/lib/cache/cache_manager.dart b/packages/smooth_app/lib/cache/cache_manager.dart deleted file mode 100644 index 316cda982e9..00000000000 --- a/packages/smooth_app/lib/cache/cache_manager.dart +++ /dev/null @@ -1,28 +0,0 @@ -/// Abstract class defining a cache containing items of type [T] and accessible -/// via a unique key of type [K] -abstract class CacheMap { - /// Saves an item - /// Returns [true] if the item was saved - Future put( - K key, - T item, { - bool overrideExistingItem = true, - }); - - /// Gets an item by its key - /// Returns [null] if it doesn't exist - Future get(K key); - - /// Returns if an item is available in the cache by giving its [key] - Future containsKey(K key); - - /// Returns the number of items - Future get length; - - /// Removes an item by its key - /// Returns [true] if the item existed and is now removed - Future remove(K key); - - /// Removes all items - Future clear(); -} diff --git a/packages/smooth_app/lib/cache/files/file_cache_manager.dart b/packages/smooth_app/lib/cache/files/file_cache_manager.dart deleted file mode 100644 index a52822b8e33..00000000000 --- a/packages/smooth_app/lib/cache/files/file_cache_manager.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:typed_data'; - -import 'package:path_provider/path_provider.dart'; -import 'package:smooth_app/cache/cache_manager.dart'; -import 'package:smooth_app/cache/files/file_cache_manager_impl.dart'; - -/// Files caches work with a [String] key. -/// This key being the filename. -typedef FileCache = CacheMap; - -/// Cache allowing to store files in the device persistent storage. -/// -/// Each cache is lazily created/loaded, meaning that even if [temporary] or -/// [persistent] is called, nothing will be created until a sub-method -/// like [get], [length]… is used. -class FileCacheManager { - const FileCacheManager._(); - - static FileCache? _temporaryCache; - static FileCache? _longLivingCache; - - static FileCache get temporary { - _temporaryCache ??= FileCacheImpl( - rootDirectory: getTemporaryDirectory(), - subFolderName: 'temp', - ); - - return _temporaryCache!; - } - - static FileCache get persistent { - _longLivingCache ??= FileCacheImpl( - rootDirectory: getApplicationDocumentsDirectory(), - subFolderName: 'persistent', - ); - - return _longLivingCache!; - } -} diff --git a/packages/smooth_app/lib/cache/files/file_cache_manager_impl.dart b/packages/smooth_app/lib/cache/files/file_cache_manager_impl.dart deleted file mode 100644 index eebe0ac1a2d..00000000000 --- a/packages/smooth_app/lib/cache/files/file_cache_manager_impl.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:path/path.dart'; -import 'package:smooth_app/cache/files/file_cache_manager.dart'; - -/// A FileCache implementation where files are stored in -/// [rootDirectory]/files_cache/[subFolderName] -class FileCacheImpl extends FileCache { - FileCacheImpl({ - required this.rootDirectory, - required this.subFolderName, - }) : assert(subFolderName.isNotEmpty); - - final Future rootDirectory; - final String subFolderName; - - /// This field will be lazily assigned after a call to [initCache] - Directory? _directory; - - @protected - Future initCache() async { - if (_directory == null) { - _directory = Directory( - join( - await rootDirectory.then((Directory d) => d.absolute.path), - 'files_cache', - subFolderName, - ), - ); - await _directory!.create(recursive: true); - } - } - - Future _ensureInitialized() async { - if (_directory == null) { - await initCache(); - } - } - - Future _getFilePath(String key) async { - await _ensureInitialized(); - return File(join(_directory!.absolute.path, key)); - } - - @override - Future get(String key) async { - final File file = await _getFilePath(key); - - if (file.existsSync()) { - return file.readAsBytes(); - } else { - return null; - } - } - - @override - Future containsKey(String key) { - return _getFilePath(key).then((File file) => file.existsSync()); - } - - @override - Future put( - String key, - Uint8List item, { - bool overrideExistingItem = true, - }) async { - if (await containsKey(key) && !overrideExistingItem) { - return false; - } - - // Will erase existing file - final File file = await _getFilePath(key); - - try { - await file.writeAsBytes( - item, - mode: FileMode.writeOnly, - flush: true, - ); - } on FileSystemException { - return false; - } - - return true; - } - - @override - Future get length => _ensureInitialized().then( - (_) => _directory!.list().length, - ); - - @override - Future remove(String key) async { - if (await containsKey(key)) { - await _getFilePath(key).then((File file) => file.delete()); - return true; - } - - return false; - } - - @override - Future clear() async { - await _ensureInitialized(); - - // Only delete content, not the folder itself - final List files = - await _directory!.list(recursive: true).toList(); - - for (final FileSystemEntity file in files) { - await file.delete(); - } - } -} diff --git a/packages/smooth_app/lib/cards/category_cards/asset_cache_helper.dart b/packages/smooth_app/lib/cards/category_cards/asset_cache_helper.dart index 58699bc1160..718569230a2 100644 --- a/packages/smooth_app/lib/cards/category_cards/asset_cache_helper.dart +++ b/packages/smooth_app/lib/cards/category_cards/asset_cache_helper.dart @@ -9,6 +9,7 @@ class AssetCacheHelper { this.width, this.height, this.color, + this.semanticsLabel, }); /// Full asset names, e.g. 'assets/cache/ab-agriculture-biologique.74x90.svg' @@ -21,9 +22,15 @@ class AssetCacheHelper { final double? height; final Color? color; - Widget getEmptySpace() => SizedBox( - width: width ?? height, - height: height ?? width, + final String? semanticsLabel; + + Widget getEmptySpace() => Semantics( + label: semanticsLabel, + image: true, + child: SizedBox( + width: width ?? height, + height: height ?? width, + ), ); void notFound() => Logs.d( diff --git a/packages/smooth_app/lib/cards/category_cards/svg_cache.dart b/packages/smooth_app/lib/cards/category_cards/svg_cache.dart index 4d07818a8d7..8b955669a9e 100644 --- a/packages/smooth_app/lib/cards/category_cards/svg_cache.dart +++ b/packages/smooth_app/lib/cards/category_cards/svg_cache.dart @@ -3,6 +3,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:smooth_app/cards/category_cards/abstract_cache.dart'; import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart'; import 'package:smooth_app/cards/category_cards/svg_safe_network.dart'; +import 'package:smooth_app/query/product_query.dart'; /// Widget that displays a svg from network (and cache while waiting). class SvgCache extends AbstractCache { @@ -11,9 +12,11 @@ class SvgCache extends AbstractCache { super.width, super.height, this.color, + this.semanticsLabel, }); final Color? color; + final String? semanticsLabel; @override List getCachedFilenames() { @@ -57,6 +60,7 @@ class SvgCache extends AbstractCache { width: width, height: height, color: forcedColor, + semanticsLabel: semanticsLabel, ); return SvgSafeNetwork( helper, @@ -67,7 +71,8 @@ class SvgCache extends AbstractCache { static String? getSemanticsLabel(BuildContext context, String iconUrl) { final AppLocalizations localizations = AppLocalizations.of(context); - return switch (Uri.parse(iconUrl).pathSegments.last) { + final String fileName = Uri.parse(iconUrl).pathSegments.last; + return switch (fileName) { 'ecoscore-a.svg' => localizations.ecoscore_a, 'ecoscore-b.svg' => localizations.ecoscore_b, 'ecoscore-c.svg' => localizations.ecoscore_c, @@ -80,14 +85,98 @@ class SvgCache extends AbstractCache { 'nova-group-3.svg' => localizations.nova_group_3, 'nova-group-4.svg' => localizations.nova_group_4, 'nova-group-unknown.svg' => localizations.nova_group_unknown, - 'nutriscore-a.svg' => localizations.nutriscore_a, - 'nutriscore-b.svg' => localizations.nutriscore_b, - 'nutriscore-c.svg' => localizations.nutriscore_c, - 'nutriscore-d.svg' => localizations.nutriscore_d, - 'nutriscore-e.svg' => localizations.nutriscore_e, - 'nutriscore-unknown.svg' => localizations.nutriscore_unknown, - 'nutriscore-not-applicable.svg' => localizations.ecoscore_not_applicable, + String _ when fileName.startsWith('nutriscore-') => + _extractNutriScore(localizations, fileName), _ => null, }; } + + static String _extractNutriScore( + AppLocalizations localizations, + String fileName, + ) { + // Old NutriScore + if (fileName == 'nutriscore-a.svg') { + return localizations.nutriscore_a; + } else if (fileName == 'nutriscore-b.svg') { + return localizations.nutriscore_b; + } else if (fileName == 'nutriscore-c.svg') { + return localizations.nutriscore_c; + } else if (fileName == 'nutriscore-d.svg') { + return localizations.nutriscore_d; + } else if (fileName == 'nutriscore-e.svg') { + return localizations.nutriscore_e; + } else if (fileName == 'nutriscore-unknown.svg') { + return localizations.nutriscore_unknown; + } else if (fileName == 'nutriscore-not-applicable.svg') { + return localizations.nutriscore_not_applicable; + } + + // NutriScore V2 + if (fileName == 'nutriscore-unknown-') { + return localizations.nutriscore_unknown_new_formula; + } else if (fileName == 'nutriscore-not-applicable-') { + return localizations.nutriscore_not_applicable_new_formula; + } else { + final String? letter; + + if (fileName.startsWith('nutriscore-a-new')) { + letter = 'A'; + } else if (fileName.startsWith('nutriscore-b-new')) { + letter = 'B'; + } else if (fileName.startsWith('nutriscore-c-new')) { + letter = 'C'; + } else if (fileName.startsWith('nutriscore-d-new')) { + letter = 'D'; + } else if (fileName.startsWith('nutriscore-e-new')) { + letter = 'E'; + } else { + return localizations.nutriscore_unknown; + } + + return localizations.nutriscore_new_formula(letter); + } + } + + static String getAssetsCacheForNutriscore( + NutriScoreValue nutriScore, + bool newNutriScore, + ) { + String suffix = ''; + if (newNutriScore) { + final StringBuffer buffer = StringBuffer('-new-'); + + buffer.write(switch (ProductQuery.getLanguage().offTag) { + 'de' => 'de', + 'en' => 'en', + 'fr' => 'fr', + 'lb' => 'lb', + 'nl' => 'nl', + _ => 'en', + }); + + suffix = buffer.toString(); + } + + return switch (nutriScore) { + NutriScoreValue.a => 'assets/cache/nutriscore-a$suffix.svg', + NutriScoreValue.b => 'assets/cache/nutriscore-b$suffix.svg', + NutriScoreValue.c => 'assets/cache/nutriscore-c$suffix.svg', + NutriScoreValue.d => 'assets/cache/nutriscore-d$suffix.svg', + NutriScoreValue.e => 'assets/cache/nutriscore-e$suffix.svg', + NutriScoreValue.notApplicable => + 'assets/cache/nutriscore-not-applicable$suffix.svg', + NutriScoreValue.unknown => 'assets/cache/nutriscore-unknown$suffix.svg', + }; + } +} + +enum NutriScoreValue { + a, + b, + c, + d, + e, + unknown, + notApplicable, } diff --git a/packages/smooth_app/lib/cards/category_cards/svg_safe_network.dart b/packages/smooth_app/lib/cards/category_cards/svg_safe_network.dart index a0892f685bb..61c81fd365d 100644 --- a/packages/smooth_app/lib/cards/category_cards/svg_safe_network.dart +++ b/packages/smooth_app/lib/cards/category_cards/svg_safe_network.dart @@ -30,6 +30,7 @@ class _SvgSafeNetworkState extends State { late final Future _loading = _load(); String get _url => widget.helper.url; + // TODO(monsieurtanuki): Change /dist/ url to be the first try when the majority of products have been updated /// Loads the SVG file from url or from alternate url. /// @@ -116,7 +117,8 @@ class _SvgSafeNetworkState extends State { ui.BlendMode.srcIn, ), fit: BoxFit.contain, - semanticsLabel: SvgCache.getSemanticsLabel(context, _url), + semanticsLabel: widget.helper.semanticsLabel ?? + SvgCache.getSemanticsLabel(context, _url), placeholderBuilder: (BuildContext context) => SvgAsyncAsset(widget.helper), ); diff --git a/packages/smooth_app/lib/cards/data_cards/product_image_carousel_item.dart b/packages/smooth_app/lib/cards/data_cards/product_image_carousel_item.dart index 029b4b8d458..d25e8afec69 100644 --- a/packages/smooth_app/lib/cards/data_cards/product_image_carousel_item.dart +++ b/packages/smooth_app/lib/cards/data_cards/product_image_carousel_item.dart @@ -31,7 +31,7 @@ class ProductImageCarouselItem extends StatefulWidget { class _ProductImageCarouselItemState extends State { @override Widget build(BuildContext context) { - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); context.watch(); final ImageProvider? imageProvider = TransientFile.fromProductImageData( diff --git a/packages/smooth_app/lib/cards/data_cards/score_card.dart b/packages/smooth_app/lib/cards/data_cards/score_card.dart index 17ca032141b..e45143d3bda 100644 --- a/packages/smooth_app/lib/cards/data_cards/score_card.dart +++ b/packages/smooth_app/lib/cards/data_cards/score_card.dart @@ -48,7 +48,8 @@ class ScoreCard extends StatelessWidget { required Attribute attribute, required this.isClickable, this.margin, - }) : iconUrl = attribute.iconUrl, + }) : type = ScoreCardType.attribute, + iconUrl = attribute.iconUrl, description = attribute.descriptionShort ?? attribute.description ?? '', cardEvaluation = getCardEvaluationFromAttribute(attribute); @@ -56,7 +57,8 @@ class ScoreCard extends StatelessWidget { required TitleElement titleElement, required this.isClickable, this.margin, - }) : iconUrl = titleElement.iconUrl, + }) : type = ScoreCardType.title, + iconUrl = titleElement.iconUrl, description = titleElement.title, cardEvaluation = getCardEvaluationFromKnowledgePanelTitleElement(titleElement); @@ -66,6 +68,7 @@ class ScoreCard extends StatelessWidget { final CardEvaluation cardEvaluation; final bool isClickable; final EdgeInsetsGeometry? margin; + final ScoreCardType type; @override Widget build(BuildContext context) { @@ -85,7 +88,8 @@ class ScoreCard extends StatelessWidget { return Semantics( value: _generateSemanticsValue(context), excludeSemantics: true, - button: true, + header: type == ScoreCardType.title, + button: isClickable, child: Padding( padding: margin ?? const EdgeInsets.symmetric(vertical: SMALL_SPACE), child: Ink( @@ -124,6 +128,10 @@ class ScoreCard extends StatelessWidget { } String _generateSemanticsValue(BuildContext context) { + if (type == ScoreCardType.title) { + return description; + } + final String? iconLabel = SvgCache.getSemanticsLabel(context, iconUrl!); if (iconLabel == null) { @@ -133,3 +141,8 @@ class ScoreCard extends StatelessWidget { } } } + +enum ScoreCardType { + title, + attribute, +} diff --git a/packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart b/packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart index f993419478c..6fa2c4f6947 100644 --- a/packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart +++ b/packages/smooth_app/lib/cards/product_cards/smooth_product_card_found.dart @@ -35,7 +35,7 @@ class SmoothProductCardFound extends StatelessWidget { final UserPreferences userPreferences = context.watch(); final ProductPreferences productPreferences = context.watch(); - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); final ThemeData themeData = Theme.of(context); final bool isDarkMode = themeData.colorScheme.brightness == Brightness.dark; final List excludedAttributeIds = diff --git a/packages/smooth_app/lib/cards/product_cards/smooth_product_card_template.dart b/packages/smooth_app/lib/cards/product_cards/smooth_product_card_template.dart index e0630a85ab0..5c556c3355f 100644 --- a/packages/smooth_app/lib/cards/product_cards/smooth_product_card_template.dart +++ b/packages/smooth_app/lib/cards/product_cards/smooth_product_card_template.dart @@ -24,7 +24,7 @@ class SmoothProductCardTemplate extends StatelessWidget { @override Widget build(BuildContext context) { - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); final ThemeData themeData = Theme.of(context); final bool isDarkMode = themeData.colorScheme.brightness == Brightness.dark; final Color itemColor = isDarkMode ? PRIMARY_GREY_COLOR : LIGHT_GREY_COLOR; diff --git a/packages/smooth_app/lib/data_models/location_list_supplier.dart b/packages/smooth_app/lib/data_models/location_list_supplier.dart new file mode 100644 index 00000000000..032e7a1a913 --- /dev/null +++ b/packages/smooth_app/lib/data_models/location_list_supplier.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:smooth_app/query/product_query.dart'; + +/// Asynchronously loads locations. +class LocationListSupplier { + LocationListSupplier( + this.query, + ); + + final String query; + + final List locations = []; + + /// Returns null if OK, or the message error + Future asyncLoad() async { + // don't ask me why, but it looks like we need to explicitly set a language, + // or else we get different (and not relevant) results + // and only en,fr,de can be used. + OpenFoodFactsLanguage getQueryLanguage() => + switch (ProductQuery.getLanguage()) { + OpenFoodFactsLanguage.FRENCH => OpenFoodFactsLanguage.FRENCH, + OpenFoodFactsLanguage.GERMAN => OpenFoodFactsLanguage.GERMAN, + OpenFoodFactsLanguage.ENGLISH => OpenFoodFactsLanguage.ENGLISH, + _ => OpenFoodFactsLanguage.ENGLISH, + }; + + try { + locations.clear(); + final http.Response response = await http.get( + Uri( + scheme: 'https', + host: 'photon.komoot.io', + path: 'api', + queryParameters: { + 'q': query, + 'lang': getQueryLanguage().offTag, + }, + ), + ); + if (response.statusCode != 200) { + return 'Could not retrieve locations'; + } + final Map map = json.decode(response.body); + if (map['type'] != 'FeatureCollection') { + return 'Unexpected result type: ${map['type']}'; + } + final List features = map['features']; + for (final Map item in features) { + final Map properties = item['properties']; + final LocationOSMType? osmType = _convertType(properties['osm_type']); + if (osmType == null) { + continue; + } + final Map geometry = item['geometry']; + final String type = geometry['type']; + if (type != 'Point') { + continue; + } + final List coordinates = geometry['coordinates']; + final double longitude = coordinates[0] as double; + final double latitude = coordinates[1] as double; + final int osmId = properties['osm_id']; + final String? name = properties['name']; + final String? street = properties['street']; + final String? city = properties['city']; + final String? countryCode = properties['countrycode']; + final String? country = properties['country']; + final String? postCode = properties['postcode']; + final OsmLocation osmLocation = OsmLocation( + osmId: osmId, + osmType: osmType, + longitude: longitude, + latitude: latitude, + name: name, + city: city, + postcode: postCode, + street: street, + country: country, + countryCode: countryCode, + ); + locations.add(osmLocation); + } + } catch (e) { + locations.clear(); + return e.toString(); + } + return null; + } + + static LocationOSMType? _convertType(final String type) => switch (type) { + 'W' => LocationOSMType.way, + 'N' => LocationOSMType.node, + 'R' => LocationOSMType.relation, + _ => null, + }; +} diff --git a/packages/smooth_app/lib/data_models/location_query_model.dart b/packages/smooth_app/lib/data_models/location_query_model.dart new file mode 100644 index 00000000000..1ec9b11b305 --- /dev/null +++ b/packages/smooth_app/lib/data_models/location_query_model.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:smooth_app/data_models/location_list_supplier.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:smooth_app/pages/product/common/loading_status.dart'; + +/// Location query model. +class LocationQueryModel with ChangeNotifier { + LocationQueryModel(this.query) { + _asyncLoad(notify: true); + } + + final String query; + + late LoadingStatus _loadingStatus; + String? _loadingError; + List displayedResults = []; + + bool isEmpty() => displayedResults.isEmpty; + + String? get loadingError => _loadingError; + LoadingStatus get loadingStatus => _loadingStatus; + + late final LocationListSupplier supplier = LocationListSupplier(query); + + Future _asyncLoad({ + final bool notify = false, + final bool fromScratch = false, + }) async { + _loadingStatus = LoadingStatus.LOADING; + _loadingError = await supplier.asyncLoad(); + if (_loadingError != null) { + _loadingStatus = LoadingStatus.ERROR; + } else { + await _process(supplier.locations, fromScratch); + _loadingStatus = LoadingStatus.LOADED; + } + if (notify) { + notifyListeners(); + } + return _loadingStatus == LoadingStatus.LOADED; + } + + Future _process( + final List locations, + final bool fromScratch, + ) async { + if (fromScratch) { + displayedResults.clear(); + } + displayedResults.addAll(locations); + _loadingStatus = LoadingStatus.LOADED; + } +} diff --git a/packages/smooth_app/lib/data_models/news_feed/newsfeed_json.dart b/packages/smooth_app/lib/data_models/news_feed/newsfeed_json.dart new file mode 100644 index 00000000000..936c0f3e199 --- /dev/null +++ b/packages/smooth_app/lib/data_models/news_feed/newsfeed_json.dart @@ -0,0 +1,428 @@ +part of 'newsfeed_provider.dart'; + +/// Content from the JSON and converted to what's in "newsfeed_model.dart" + +class _TagLineJSON { + _TagLineJSON.fromJson(Map json) + : news = (json['news'] as Map).map( + (dynamic id, dynamic value) => MapEntry( + id, + _TagLineItemNewsItem.fromJson(id, value), + ), + ), + taglineFeed = _TaglineJSONFeed.fromJson(json['tagline_feed']); + + final _TagLineJSONNewsList news; + final _TaglineJSONFeed taglineFeed; + + AppNews toTagLine(String locale) { + final Map tagLineNews = news.map( + (String key, _TagLineItemNewsItem value) => MapEntry( + key, + value.toTagLineItem(locale), + ), + ); + + final _TagLineJSONFeedLocale localizedFeed = taglineFeed.loadNews(locale); + final Iterable feed = localizedFeed.news + .map((_TagLineJSONFeedLocaleItem item) { + if (news[item.id] == null) { + // The asked ID doesn't exist in the news + return null; + } + return item.overrideNewsItem(news[item.id]!, locale); + }) + .where((AppNewsFeedItem? item) => + item != null && + (item.startDate == null || + item.startDate!.isBefore(DateTime.now())) && + (item.endDate == null || item.endDate!.isAfter(DateTime.now()))) + .whereNotNull(); + + return AppNews( + news: AppNewsList(tagLineNews), + feed: AppNewsFeed( + feed.toList(growable: false), + ), + ); + } +} + +typedef _TagLineJSONNewsList = Map; + +class _TagLineItemNewsItem { + const _TagLineItemNewsItem._({ + required this.id, + required this.url, + required _TagLineItemNewsTranslations translations, + this.startDate, + this.endDate, + this.style, + }) : _translations = translations; + + _TagLineItemNewsItem.fromJson(this.id, Map json) + : assert((json['url'] as String).isNotEmpty), + url = json['url'], + assert((json['translations'] as Map) + .containsKey('default')), + _translations = (json['translations'] as Map) + .map((dynamic key, dynamic value) { + if (key == 'default') { + return MapEntry( + key, _TagLineItemNewsTranslationDefault.fromJson(value)); + } else { + return MapEntry( + key, + _TagLineItemNewsTranslation.fromJson(value), + ); + } + }), + startDate = DateTime.tryParse(json['start_date']), + endDate = DateTime.tryParse(json['end_date']), + style = json['style'] == null + ? null + : _TagLineNewsStyle.fromJson(json['style']); + + final String id; + final String url; + final _TagLineItemNewsTranslations _translations; + final DateTime? startDate; + final DateTime? endDate; + final _TagLineNewsStyle? style; + + _TagLineItemNewsTranslation loadTranslation(String locale) { + _TagLineItemNewsTranslation? translation; + // Direct match + if (_translations.containsKey(locale)) { + translation = _translations[locale]; + } else if (locale.contains('_')) { + final List splittedLocale = locale.split('_'); + final String languageCode = splittedLocale.first; + final String countryCode = '_${splittedLocale.last}'; + if (_translations.containsKey(languageCode)) { + translation = _translations[languageCode]; + } else if (_translations.containsKey(countryCode)) { + translation = _translations[countryCode]; + } + } + + return _translations['default']!.merge(translation); + } + + AppNewsItem toTagLineItem(String locale) { + final _TagLineItemNewsTranslation translation = loadTranslation(locale); + // We can assume the default translation has a non-null title and message + return AppNewsItem( + id: id, + title: translation.title!, + message: translation.message!, + url: translation.url ?? url, + buttonLabel: translation.buttonLabel, + startDate: startDate, + endDate: endDate, + style: style?.toTagLineStyle(), + image: translation.image?.overridesContent == true + ? translation.image?.toTagLineImage() + : null, + ); + } + + _TagLineItemNewsItem copyWith({ + String? url, + _TagLineItemNewsTranslations? translations, + DateTime? startDate, + DateTime? endDate, + _TagLineNewsStyle? style, + }) { + return _TagLineItemNewsItem._( + id: id, + // Still the same + url: url ?? this.url, + translations: translations ?? _translations, + startDate: startDate ?? this.startDate, + endDate: endDate ?? this.endDate, + style: style ?? this.style, + ); + } +} + +typedef _TagLineItemNewsTranslations = Map; + +class _TagLineItemNewsTranslation { + _TagLineItemNewsTranslation._({ + this.title, + this.message, + this.url, + this.buttonLabel, + this.image, + }); + + _TagLineItemNewsTranslation.fromJson(Map json) + : assert(json['title'] == null || (json['title'] as String).isNotEmpty), + assert( + json['message'] == null || (json['message'] as String).isNotEmpty), + assert(json['url'] == null || (json['url'] as String).isNotEmpty), + assert(json['button_label'] == null || + (json['button_label'] as String).isNotEmpty), + title = json['title'], + message = json['message'], + url = json['url'], + buttonLabel = json['button_label'], + image = json['image'] == null + ? null + : _TagLineNewsImage.fromJson(json['image']); + final String? title; + final String? message; + final String? url; + final String? buttonLabel; + final _TagLineNewsImage? image; + + _TagLineItemNewsTranslation copyWith({ + String? title, + String? message, + String? url, + String? buttonLabel, + _TagLineNewsImage? image, + }) { + return _TagLineItemNewsTranslation._( + title: title ?? this.title, + message: message ?? this.message, + url: url ?? this.url, + buttonLabel: buttonLabel ?? this.buttonLabel, + image: image ?? this.image, + ); + } + + _TagLineItemNewsTranslation merge(_TagLineItemNewsTranslation? other) { + if (other == null) { + return this; + } + + return copyWith( + title: other.title, + message: other.message, + url: other.url, + buttonLabel: other.buttonLabel, + image: other.image, + ); + } +} + +class _TagLineItemNewsTranslationDefault extends _TagLineItemNewsTranslation { + _TagLineItemNewsTranslationDefault.fromJson(Map json) + : assert((json['title'] as String).isNotEmpty), + assert((json['message'] as String).isNotEmpty), + assert(json['image'] == null || + ((json['image'] as Map)['url'] as String) + .isNotEmpty), + super.fromJson(json); +} + +class _TagLineNewsImage { + _TagLineNewsImage.fromJson(Map json) + : assert(json['width'] == null || + ((json['width'] as num) >= 0.0 && (json['width'] as num) <= 1.0)), + assert(json['alt'] == null || (json['alt'] as String).isNotEmpty), + url = json['url'], + width = json['width'], + alt = json['alt']; + + final String? url; + final double? width; + final String? alt; + + AppNewsImage toTagLineImage() { + return AppNewsImage( + src: url, + width: width, + alt: alt, + ); + } + + bool get overridesContent => url != null || width != null || alt != null; +} + +class _TagLineNewsStyle { + _TagLineNewsStyle._({ + this.titleBackground, + this.titleTextColor, + this.titleIndicatorColor, + this.messageBackground, + this.messageTextColor, + this.buttonBackground, + this.buttonTextColor, + this.contentBackgroundColor, + }); + + _TagLineNewsStyle.fromJson(Map json) + : assert(json['title_background'] == null || + (json['title_background'] as String).startsWith('#')), + assert(json['title_text_color'] == null || + (json['title_text_color'] as String).startsWith('#')), + assert(json['title_indicator_color'] == null || + (json['title_indicator_color'] as String).startsWith('#')), + assert(json['message_background'] == null || + (json['message_background'] as String).startsWith('#')), + assert(json['message_text_color'] == null || + (json['message_text_color'] as String).startsWith('#')), + assert(json['button_background'] == null || + (json['button_background'] as String).startsWith('#')), + assert(json['button_text_color'] == null || + (json['button_text_color'] as String).startsWith('#')), + assert(json['content_background_color'] == null || + (json['content_background_color'] as String).startsWith('#')), + titleBackground = json['title_background'], + titleTextColor = json['title_text_color'], + titleIndicatorColor = json['title_indicator_color'], + messageBackground = json['message_background'], + messageTextColor = json['message_text_color'], + buttonBackground = json['button_background'], + buttonTextColor = json['button_text_color'], + contentBackgroundColor = json['content_background_color']; + + final String? titleBackground; + final String? titleTextColor; + final String? titleIndicatorColor; + final String? messageBackground; + final String? messageTextColor; + final String? buttonBackground; + final String? buttonTextColor; + final String? contentBackgroundColor; + + _TagLineNewsStyle copyWith({ + String? titleBackground, + String? titleTextColor, + String? titleIndicatorColor, + String? messageBackground, + String? messageTextColor, + String? buttonBackground, + String? buttonTextColor, + String? contentBackgroundColor, + }) { + return _TagLineNewsStyle._( + titleBackground: titleBackground ?? this.titleBackground, + titleTextColor: titleTextColor ?? this.titleTextColor, + titleIndicatorColor: titleIndicatorColor ?? this.titleIndicatorColor, + messageBackground: messageBackground ?? this.messageBackground, + messageTextColor: messageTextColor ?? this.messageTextColor, + buttonBackground: buttonBackground ?? this.buttonBackground, + buttonTextColor: buttonTextColor ?? this.buttonTextColor, + contentBackgroundColor: + contentBackgroundColor ?? this.contentBackgroundColor, + ); + } + + AppNewsStyle toTagLineStyle() => AppNewsStyle.fromHexa( + titleBackground: titleBackground, + titleTextColor: titleTextColor, + titleIndicatorColor: titleIndicatorColor, + messageBackground: messageBackground, + messageTextColor: messageTextColor, + buttonBackground: buttonBackground, + buttonTextColor: buttonTextColor, + contentBackgroundColor: contentBackgroundColor, + ); +} + +class _TaglineJSONFeed { + _TaglineJSONFeed.fromJson(Map json) + : assert(json.containsKey('default')), + _news = json.map( + (dynamic key, dynamic value) => + MapEntry( + key, + _TagLineJSONFeedLocale.fromJson(value), + ), + ); + + final _TagLineJSONFeedList _news; + + _TagLineJSONFeedLocale loadNews(String locale) { + // Direct match + if (_news.containsKey(locale)) { + return _news[locale]!; + } + + // Try by language + if (locale.contains('_')) { + final List splittedLocale = locale.split('_'); + final String languageCode = splittedLocale.first; + final String countryCode = '_${splittedLocale.last}'; + if (_news.containsKey(languageCode)) { + return _news[languageCode]!; + } else if (_news.containsKey(countryCode)) { + return _news[countryCode]!; + } + } + + return _news['default']!; + } +} + +typedef _TagLineJSONFeedList = Map; + +class _TagLineJSONFeedLocale { + _TagLineJSONFeedLocale.fromJson(Map json) + : assert(json['news'] is Iterable), + news = (json['news'] as Iterable) + .map((dynamic json) => _TagLineJSONFeedLocaleItem.fromJson(json)); + + final Iterable<_TagLineJSONFeedLocaleItem> news; +} + +class _TagLineJSONFeedLocaleItem { + _TagLineJSONFeedLocaleItem.fromJson(Map json) + : assert((json['id'] as String).isNotEmpty), + id = json['id'], + overrideContent = json['override'] != null + ? _TagLineJSONFeedNewsItemOverride.fromJson( + json['override'] as Map) + : null; + + final String id; + final _TagLineJSONFeedNewsItemOverride? overrideContent; + + AppNewsFeedItem overrideNewsItem( + _TagLineItemNewsItem newsItem, + String locale, + ) { + _TagLineItemNewsItem item = newsItem; + + if (overrideContent != null) { + item = newsItem.copyWith( + url: overrideContent!.url ?? newsItem.url, + startDate: overrideContent!.startDate ?? newsItem.startDate, + endDate: overrideContent!.endDate ?? newsItem.endDate, + style: overrideContent!.style ?? newsItem.style, + ); + } + + final AppNewsItem tagLineItem = item.toTagLineItem(locale); + + return AppNewsFeedItem( + news: tagLineItem, + startDate: tagLineItem.startDate, + endDate: tagLineItem.endDate, + ); + } +} + +class _TagLineJSONFeedNewsItemOverride { + _TagLineJSONFeedNewsItemOverride.fromJson(Map json) + : assert(json['url'] == null || (json['url'] as String).isNotEmpty), + url = json['url'], + startDate = json['start_date'] != null + ? DateTime.tryParse(json['start_date']) + : null, + endDate = json['end_date'] != null + ? DateTime.tryParse(json['end_date']) + : null, + style = json['style'] == null + ? null + : _TagLineNewsStyle.fromJson(json['style']); + + final String? url; + final DateTime? startDate; + final DateTime? endDate; + final _TagLineNewsStyle? style; +} diff --git a/packages/smooth_app/lib/data_models/news_feed/newsfeed_model.dart b/packages/smooth_app/lib/data_models/news_feed/newsfeed_model.dart new file mode 100644 index 00000000000..a4790124990 --- /dev/null +++ b/packages/smooth_app/lib/data_models/news_feed/newsfeed_model.dart @@ -0,0 +1,166 @@ +import 'dart:ui'; + +class AppNews { + const AppNews({ + required this.news, + required this.feed, + }); + + final AppNewsList news; + final AppNewsFeed feed; + + bool get hasContent => news._news.isNotEmpty && feed.news.isNotEmpty; + + @override + String toString() { + return 'AppNews{news: $news, feed: $feed}'; + } +} + +class AppNewsList { + const AppNewsList(Map news) : _news = news; + + final Map _news; + + AppNewsItem? operator [](String key) => _news[key]; + + @override + String toString() { + return 'AppNewsList{_news: $_news}'; + } +} + +class AppNewsItem { + const AppNewsItem({ + required this.id, + required this.title, + required this.message, + required this.url, + this.buttonLabel, + this.startDate, + this.endDate, + this.image, + this.style, + }); + + final String id; + final String title; + final String message; + final String url; + final String? buttonLabel; + final DateTime? startDate; + final DateTime? endDate; + final AppNewsImage? image; + final AppNewsStyle? style; + + @override + String toString() { + return 'AppNewsItem{id: $id, title: $title, message: $message, url: $url, buttonLabel: $buttonLabel, startDate: $startDate, endDate: $endDate, image: $image, style: $style}'; + } +} + +class AppNewsStyle { + const AppNewsStyle({ + this.titleBackground, + this.titleTextColor, + this.titleIndicatorColor, + this.messageBackground, + this.messageTextColor, + this.buttonBackground, + this.buttonTextColor, + this.contentBackgroundColor, + }); + + AppNewsStyle.fromHexa({ + String? titleBackground, + String? titleTextColor, + String? titleIndicatorColor, + String? messageBackground, + String? messageTextColor, + String? buttonBackground, + String? buttonTextColor, + String? contentBackgroundColor, + }) : titleBackground = _parseColor(titleBackground), + titleTextColor = _parseColor(titleTextColor), + titleIndicatorColor = _parseColor(titleIndicatorColor), + messageBackground = _parseColor(messageBackground), + messageTextColor = _parseColor(messageTextColor), + buttonBackground = _parseColor(buttonBackground), + buttonTextColor = _parseColor(buttonTextColor), + contentBackgroundColor = _parseColor(contentBackgroundColor); + + final Color? titleBackground; + final Color? titleTextColor; + final Color? titleIndicatorColor; + final Color? messageBackground; + final Color? messageTextColor; + final Color? buttonBackground; + final Color? buttonTextColor; + final Color? contentBackgroundColor; + + static Color? _parseColor(String? hexa) { + if (hexa == null || hexa.length != 7) { + return null; + } + return Color(int.parse(hexa.substring(1), radix: 16)); + } + + @override + String toString() { + return 'AppNewsStyle{titleBackground: $titleBackground, titleTextColor: $titleTextColor, titleIndicatorColor: $titleIndicatorColor, messageBackground: $messageBackground, messageTextColor: $messageTextColor, buttonBackground: $buttonBackground, buttonTextColor: $buttonTextColor, contentBackgroundColor: $contentBackgroundColor}'; + } +} + +class AppNewsImage { + const AppNewsImage({ + required this.src, + this.width, + this.alt, + }); + + final String? src; + final double? width; + final String? alt; + + @override + String toString() { + return 'AppNewsImage{src: $src, width: $width, alt: $alt}'; + } +} + +class AppNewsFeed { + const AppNewsFeed(this.news); + + final List news; + + bool get isNotEmpty => news.isNotEmpty; + + @override + String toString() { + return 'TagLineFeed{news: $news}'; + } +} + +class AppNewsFeedItem { + const AppNewsFeedItem({ + required this.news, + DateTime? startDate, + DateTime? endDate, + }) : _startDate = startDate, + _endDate = endDate; + + final AppNewsItem news; + final DateTime? _startDate; + final DateTime? _endDate; + + String get id => news.id; + + DateTime? get startDate => _startDate ?? news.startDate; + + DateTime? get endDate => _endDate ?? news.endDate; + + @override + String toString() { + return 'AppNewsFeedItem{news: $news, _startDate: $_startDate, _endDate: $_endDate}'; + } +} diff --git a/packages/smooth_app/lib/data_models/news_feed/newsfeed_provider.dart b/packages/smooth_app/lib/data_models/news_feed/newsfeed_provider.dart new file mode 100644 index 00000000000..c72b44ff146 --- /dev/null +++ b/packages/smooth_app/lib/data_models/news_feed/newsfeed_provider.dart @@ -0,0 +1,218 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:isolate'; + +import 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart'; +import 'package:http/http.dart' as http; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:smooth_app/data_models/news_feed/newsfeed_model.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart'; +import 'package:smooth_app/query/product_query.dart'; +import 'package:smooth_app/services/smooth_services.dart'; + +part 'newsfeed_json.dart'; + +/// This provides one one side a list of news and on the other a feed of news. +/// A feed contains some of the news? +/// +/// The content is fetched on the server and cached locally (1 day). +/// To be notified of changes, listen to this [ChangeNotifier] and more +/// particularly to the [state] property. +class AppNewsProvider extends ChangeNotifier { + AppNewsProvider(UserPreferences preferences) + : _state = const AppNewsStateLoading(), + _preferences = preferences, + _uriOverride = preferences.getDevModeString( + UserPreferencesDevMode.userPreferencesCustomNewsJSONURI), + _domain = preferences.getDevModeString( + UserPreferencesDevMode.userPreferencesTestEnvDomain), + _prodEnv = preferences + .getFlag(UserPreferencesDevMode.userPreferencesFlagProd) { + _preferences.addListener(_onPreferencesChanged); + loadLatestNews(); + } + + final UserPreferences _preferences; + + AppNewsState _state; + + bool get hasContent => + _state is AppNewsStateLoaded && + (_state as AppNewsStateLoaded).content.hasContent; + + Future loadLatestNews({bool forceUpdate = false}) async { + _emit(const AppNewsStateLoading()); + + final String locale = ProductQuery.getLocaleString(); + if (locale.startsWith('-')) { + // ProductQuery not ready + return; + } + + final File cacheFile = await _newsCacheFile; + String? jsonString; + // Try from the cache first + if (!forceUpdate && _isNewsCacheValid(cacheFile)) { + jsonString = cacheFile.readAsStringSync(); + } + + if (jsonString == null || jsonString.isEmpty == true) { + jsonString = await _fetchJSON(); + } + + if (jsonString?.isNotEmpty != true) { + _emit(const AppNewsStateError('JSON news file is empty')); + return; + } + + final AppNews? appNews = await Isolate.run( + () => _parseJSONAndGetLocalizedContent(jsonString!, locale)); + if (appNews == null) { + _emit(const AppNewsStateError('Unable to parse the JSON news file')); + Logs.e('Unable to parse the JSON news file'); + } else { + _emit(AppNewsStateLoaded(appNews, cacheFile.lastModifiedSync())); + Logs.i('News ${forceUpdate ? 're' : ''}loaded'); + } + } + + void _emit(AppNewsState state) { + _state = state; + WidgetsBinding.instance.addPostFrameCallback((_) { + notifyListeners(); + }); + } + + AppNewsState get state => _state; + + static Future _parseJSONAndGetLocalizedContent( + String json, + String locale, + ) async { + try { + final _TagLineJSON tagLineJSON = + _TagLineJSON.fromJson(jsonDecode(json) as Map); + return tagLineJSON.toTagLine(locale); + } catch (_) { + return null; + } + } + + /// API URL: [https://world.openfoodfacts.[org/net]/resources/files/tagline-off-ios-v3.json] + /// or [https://world.openfoodfacts.[org/net]/resources/files/tagline-off-android-v3.json] + Future _fetchJSON() async { + try { + final UriProductHelper uriProductHelper = ProductQuery.uriProductHelper; + final Map headers = {}; + final Uri uri; + + if (_uriOverride?.isNotEmpty == true) { + uri = Uri.parse(_uriOverride!); + } else { + uri = uriProductHelper.getUri(path: _newsUrl); + } + + if (uriProductHelper.userInfoForPatch != null) { + headers['Authorization'] = + 'Basic ${base64Encode(utf8.encode(uriProductHelper.userInfoForPatch!))}'; + } + + final http.Response response = await http.get(uri, headers: headers); + + if (response.statusCode == 404) { + Logs.e("Remote file $uri doesn't exist!"); + throw Exception('Incorrect URL= $uri'); + } + + final String json = const Utf8Decoder().convert(response.bodyBytes); + + if (!json.startsWith('[') && !json.startsWith('{')) { + throw Exception('Invalid JSON'); + } + await _saveNewsToCache(json); + return json; + } catch (_) { + return null; + } + } + + /// Based on the platform, the URL may differ + String get _newsUrl { + if (Platform.isIOS || Platform.isMacOS) { + return '/resources/files/tagline-off-ios-v3.json'; + } else { + return '/resources/files/tagline-off-android-v3.json'; + } + } + + Future get _newsCacheFile => getApplicationCacheDirectory() + .then((Directory dir) => File(join(dir.path, 'tagline.json'))); + + Future _saveNewsToCache(final String json) async { + final File file = await _newsCacheFile; + return file.writeAsString(json); + } + + bool _isNewsCacheValid(File file) => + file.existsSync() && + file.lengthSync() > 0 && + file + .lastModifiedSync() + .isAfter(DateTime.now().add(const Duration(days: -1))); + + bool? _prodEnv; + String? _domain; + String? _uriOverride; + + /// [ProductQuery.uriProductHelper] is not synced yet, + /// so we have to check it manually + Future _onPreferencesChanged() async { + final String jsonURI = _preferences.getDevModeString( + UserPreferencesDevMode.userPreferencesCustomNewsJSONURI) ?? + ''; + final String domain = _preferences.getDevModeString( + UserPreferencesDevMode.userPreferencesTestEnvDomain) ?? + ''; + final bool prodEnv = + _preferences.getFlag(UserPreferencesDevMode.userPreferencesFlagProd) ?? + true; + + if (domain != _domain || prodEnv != _prodEnv || jsonURI != _uriOverride) { + _domain = domain; + _prodEnv = prodEnv; + _uriOverride = jsonURI; + loadLatestNews(forceUpdate: true); + } + } + + @override + void dispose() { + _preferences.removeListener(_onPreferencesChanged); + super.dispose(); + } +} + +sealed class AppNewsState { + const AppNewsState(); +} + +final class AppNewsStateLoading extends AppNewsState { + const AppNewsStateLoading(); +} + +class AppNewsStateLoaded extends AppNewsState { + const AppNewsStateLoaded(this.content, this.lastUpdate); + + final AppNews content; + final DateTime lastUpdate; +} + +class AppNewsStateError extends AppNewsState { + const AppNewsStateError(this.exception); + + final dynamic exception; +} diff --git a/packages/smooth_app/lib/data_models/onboarding_loader.dart b/packages/smooth_app/lib/data_models/onboarding_loader.dart index 9ba991956e8..33a193971f3 100644 --- a/packages/smooth_app/lib/data_models/onboarding_loader.dart +++ b/packages/smooth_app/lib/data_models/onboarding_loader.dart @@ -44,8 +44,7 @@ class OnboardingLoader { } return; case OnboardingPage.NOT_STARTED: - case OnboardingPage.REINVENTION: - case OnboardingPage.SCAN_EXAMPLE: + case OnboardingPage.HOME_PAGE: case OnboardingPage.HEALTH_CARD_EXAMPLE: case OnboardingPage.ECO_CARD_EXAMPLE: case OnboardingPage.PREFERENCES_PAGE: diff --git a/packages/smooth_app/lib/data_models/personalized_ranking_model.dart b/packages/smooth_app/lib/data_models/personalized_ranking_model.dart index c39aaa39ec2..03b4b437e2c 100644 --- a/packages/smooth_app/lib/data_models/personalized_ranking_model.dart +++ b/packages/smooth_app/lib/data_models/personalized_ranking_model.dart @@ -3,12 +3,7 @@ import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/database/dao_product.dart'; import 'package:smooth_app/database/local_database.dart'; - -enum LoadingStatus { - LOADING, - LOADED, - ERROR, -} +import 'package:smooth_app/pages/product/common/loading_status.dart'; /// Model that computes the scores and sorts the barcodes accordingly. class PersonalizedRankingModel with ChangeNotifier { diff --git a/packages/smooth_app/lib/data_models/preferences/migration/user_preferences_migration.dart b/packages/smooth_app/lib/data_models/preferences/migration/user_preferences_migration.dart index 08a8b8b1406..5cdb4b9766d 100644 --- a/packages/smooth_app/lib/data_models/preferences/migration/user_preferences_migration.dart +++ b/packages/smooth_app/lib/data_models/preferences/migration/user_preferences_migration.dart @@ -78,7 +78,7 @@ class _UserPreferencesMigrationV2 extends UserPreferencesMigration { null) { await preferences._sharedPreferences.setInt( UserPreferences._TAG_USER_GROUP, - Random().nextInt(10), + math.Random().nextInt(10), ); } } diff --git a/packages/smooth_app/lib/data_models/preferences/user_preferences.dart b/packages/smooth_app/lib/data_models/preferences/user_preferences.dart index 0876509b572..f5c4d3aa077 100644 --- a/packages/smooth_app/lib/data_models/preferences/user_preferences.dart +++ b/packages/smooth_app/lib/data_models/preferences/user_preferences.dart @@ -1,4 +1,4 @@ -import 'dart:math'; +import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; @@ -7,6 +7,7 @@ import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/pages/onboarding/onboarding_flow_navigator.dart'; import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart'; import 'package:smooth_app/themes/color_schemes.dart'; +import 'package:smooth_app/themes/theme_provider.dart'; part 'package:smooth_app/data_models/preferences/migration/user_preferences_migration.dart'; @@ -67,12 +68,16 @@ class UserPreferences extends ChangeNotifier { static const String _TAG_CURRENT_COLOR_SCHEME = 'currentColorScheme'; static const String _TAG_CURRENT_CONTRAST_MODE = 'contrastMode'; static const String _TAG_USER_COUNTRY_CODE = 'userCountry'; + static const String _TAG_USER_COUNTRY_CODE_LAST_UPDATE = + 'userCountryLastUpdate'; + static const String _TAG_USER_CURRENCY_CODE = 'userCurrency'; static const String _TAG_LAST_VISITED_ONBOARDING_PAGE = 'lastVisitedOnboardingPage'; static const String _TAG_PREFIX_FLAG = 'FLAG_PREFIX_'; static const String _TAG_DEV_MODE = 'devMode'; static const String _TAG_USER_TRACKING = 'user_tracking'; static const String _TAG_CRASH_REPORTS = 'crash_reports'; + static const String _TAG_PRICES_FEEDBACK_FORM = 'prices_feedback_form'; static const String _TAG_EXCLUDED_ATTRIBUTE_IDS = 'excluded_attributes'; static const String _TAG_USER_GROUP = '_user_group'; static const String _TAG_UNIQUE_RANDOM = '_unique_random'; @@ -88,6 +93,9 @@ class UserPreferences extends ChangeNotifier { /// Vibrations / haptic feedback static const String _TAG_HAPTIC_FEEDBACK_IN_APP = 'haptic_feedback_enabled'; + /// Price privacy warning + static const String TAG_PRICE_PRIVACY_WARNING = 'price_privacy_warning'; + /// Attribute group that is not collapsed static const String _TAG_ACTIVE_ATTRIBUTE_GROUP = 'activeAttributeGroup'; @@ -104,6 +112,11 @@ class UserPreferences extends ChangeNotifier { static const String _TAG_USER_KNOWLEDGE_PANEL_ORDER = 'userKnowledgePanelOrder'; + /// Tagline feed (news displayed / clicked) + static const String _TAG_TAGLINE_FEED_NEWS_DISPLAYED = + 'taglineFeedNewsDisplayed'; + static const String _TAG_TAGLINE_FEED_NEWS_CLICKED = 'taglineFeedNewsClicked'; + Future init(final ProductPreferences productPreferences) async { await _onMigrate(); @@ -179,7 +192,7 @@ class UserPreferences extends ChangeNotifier { if (result != null) { return result; } - result = Random().nextInt(1 << 32); + result = math.Random().nextInt(1 << 32); await _sharedPreferences.setInt(tag, result); return result; } @@ -193,8 +206,17 @@ class UserPreferences extends ChangeNotifier { bool get crashReports => _sharedPreferences.getBool(_TAG_CRASH_REPORTS) ?? false; + Future markPricesFeedbackFormAsCompleted() async { + await _sharedPreferences.setBool(_TAG_PRICES_FEEDBACK_FORM, false); + notifyListeners(); + } + + bool get shouldShowPricesFeedbackForm => + _sharedPreferences.getBool(_TAG_PRICES_FEEDBACK_FORM) ?? true; + String get currentTheme => - _sharedPreferences.getString(_TAG_CURRENT_THEME_MODE) ?? 'System Default'; + _sharedPreferences.getString(_TAG_CURRENT_THEME_MODE) ?? + THEME_SYSTEM_DEFAULT; String get currentColor => _sharedPreferences.getString(_TAG_CURRENT_COLOR_SCHEME) ?? @@ -207,12 +229,24 @@ class UserPreferences extends ChangeNotifier { /// Please use [ProductQuery.setCountry] as interface Future setUserCountryCode(final String countryCode) async { await _sharedPreferences.setString(_TAG_USER_COUNTRY_CODE, countryCode); + await _sharedPreferences.setInt( + _TAG_USER_COUNTRY_CODE_LAST_UPDATE, + DateTime.now().millisecondsSinceEpoch, + ); notifyListeners(); } String? get userCountryCode => _sharedPreferences.getString(_TAG_USER_COUNTRY_CODE); + Future setUserCurrencyCode(final String code) async { + await _sharedPreferences.setString(_TAG_USER_CURRENCY_CODE, code); + notifyListeners(); + } + + String? get userCurrencyCode => + _sharedPreferences.getString(_TAG_USER_CURRENCY_CODE); + Future setLastVisitedOnboardingPage(final OnboardingPage page) async { await _sharedPreferences.setInt( _TAG_LAST_VISITED_ONBOARDING_PAGE, page.index); @@ -223,6 +257,7 @@ class UserPreferences extends ChangeNotifier { await setLastVisitedOnboardingPage(OnboardingPage.NOT_STARTED); // for tests with a fresh null country await _sharedPreferences.remove(_TAG_USER_COUNTRY_CODE); + await _sharedPreferences.remove(_TAG_USER_CURRENCY_CODE); notifyListeners(); } @@ -231,7 +266,8 @@ class UserPreferences extends ChangeNotifier { _sharedPreferences.getInt(_TAG_LAST_VISITED_ONBOARDING_PAGE); return pageIndex == null ? OnboardingPage.NOT_STARTED - : OnboardingPage.values[pageIndex]; + : OnboardingPage + .values[math.min(pageIndex, OnboardingPage.values.length - 1)]; } Future incrementScanCount() async { @@ -359,4 +395,56 @@ class UserPreferences extends ChangeNotifier { _TAG_USER_KNOWLEDGE_PANEL_ORDER, source); notifyListeners(); } + + List get taglineFeedDisplayedNews => + _sharedPreferences.getStringList(_TAG_TAGLINE_FEED_NEWS_DISPLAYED) ?? + []; + + List get taglineFeedClickedNews => + _sharedPreferences.getStringList(_TAG_TAGLINE_FEED_NEWS_CLICKED) ?? + []; + + // This method voluntarily does not notify listeners (not needed) + Future taglineFeedMarkNewsAsDisplayed(final String ids) async { + final List displayedNews = taglineFeedDisplayedNews; + final List clickedNews = taglineFeedClickedNews; + + if (!displayedNews.contains(ids)) { + displayedNews.add(ids); + _sharedPreferences.setStringList( + _TAG_TAGLINE_FEED_NEWS_DISPLAYED, + displayedNews, + ); + } + + if (clickedNews.contains(ids)) { + clickedNews.remove(ids); + _sharedPreferences.setStringList( + _TAG_TAGLINE_FEED_NEWS_CLICKED, + clickedNews, + ); + } + } + + // This method voluntarily does not notify listeners (not needed) + Future taglineFeedMarkNewsAsClicked(final String ids) async { + final List displayedNews = taglineFeedDisplayedNews; + final List clickedNews = taglineFeedClickedNews; + + if (displayedNews.contains(ids)) { + displayedNews.remove(ids); + _sharedPreferences.setStringList( + _TAG_TAGLINE_FEED_NEWS_DISPLAYED, + displayedNews, + ); + } + + if (!clickedNews.contains(ids)) { + clickedNews.add(ids); + _sharedPreferences.setStringList( + _TAG_TAGLINE_FEED_NEWS_CLICKED, + clickedNews, + ); + } + } } diff --git a/packages/smooth_app/lib/data_models/product_query_model.dart b/packages/smooth_app/lib/data_models/product_query_model.dart index 534b7e6101d..f9eeccb55b0 100644 --- a/packages/smooth_app/lib/data_models/product_query_model.dart +++ b/packages/smooth_app/lib/data_models/product_query_model.dart @@ -1,11 +1,6 @@ import 'package:flutter/material.dart'; import 'package:smooth_app/data_models/product_list_supplier.dart'; - -enum LoadingStatus { - LOADING, - LOADED, - ERROR, -} +import 'package:smooth_app/pages/product/common/loading_status.dart'; class ProductQueryModel with ChangeNotifier { ProductQueryModel(this._supplier) { diff --git a/packages/smooth_app/lib/data_models/tagline.dart b/packages/smooth_app/lib/data_models/tagline.dart deleted file mode 100644 index 9c392294b14..00000000000 --- a/packages/smooth_app/lib/data_models/tagline.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:http/http.dart' as http; -import 'package:openfoodfacts/openfoodfacts.dart'; -import 'package:smooth_app/helpers/collections_helper.dart'; -import 'package:smooth_app/query/product_query.dart'; - -/// A tagline is the text displayed on the homepage -/// It may contain a link to an external resource -/// No cache is expected here -/// API URL: [https://world.openfoodfacts.org/files/tagline-off-ios-v2.json] or -/// [https://world.openfoodfacts.org/files/tagline-off-android-v2.json] -Future fetchTagLine() { - final String locale = ProductQuery.getLanguage().code; - - return http - .get( - Uri.https( - 'world.openfoodfacts.org', - _tagLineUrl, - ), - ) - .then( - (http.Response value) => const Utf8Decoder().convert(value.bodyBytes)) - .then((String value) => - _TagLine.fromJSON(jsonDecode(value) as List)) - .then((_TagLine tagLine) => tagLine[locale] ?? tagLine['en']) - .catchError((dynamic err) => null); -} - -/// Based on the platform, the URL may differ -String get _tagLineUrl { - if (Platform.isIOS || Platform.isMacOS) { - return '/files/tagline-off-ios-v2.json'; - } else { - return '/files/tagline-off-android-v2.json'; - } -} - -class _TagLine { - _TagLine.fromJSON(List json) - : _items = Map.fromEntries( - json.map( - (dynamic element) { - return MapEntry( - ((element as Map)['language'] as String) - .toLowerCase(), - TagLineItem._fromJSON(element['data'] as Map), - ); - }, - ), - ); - - /// Taglines by their locale - final Map _items; - - /// Finds a tagline with its locale - TagLineItem? operator [](String key) { - final String locale = key.toLowerCase(); - - // Let's try with the full locale - if (_items.containsKey(locale)) { - return _items[locale]; - } - - // Let's try with the language only (eg => fr_FR to fr) - final String languageCode = locale.substring(0, 2); - - if (_items.containsKey(languageCode)) { - return _items[languageCode]; - } else { - // Finally let's try with a subset (eg => no fr_BE but fr_FR) - return _items.getValueByKeyStartWith(languageCode, ignoreCase: true); - } - } -} - -class TagLineItem { - TagLineItem._fromJSON(Map json) - : url = json['url'] as String, - message = json['message'] as String; - - final String url; - final String message; - - bool get hasLink => url.startsWith('http'); -} diff --git a/packages/smooth_app/lib/data_models/user_management_provider.dart b/packages/smooth_app/lib/data_models/user_management_provider.dart index e1d02a40d8a..57499661469 100644 --- a/packages/smooth_app/lib/data_models/user_management_provider.dart +++ b/packages/smooth_app/lib/data_models/user_management_provider.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; @@ -45,7 +47,7 @@ class UserManagementProvider with ChangeNotifier { try { effectiveUserId = userId ?? await DaoSecuredString.get(_USER_ID); effectivePassword = password ?? await DaoSecuredString.get(_PASSWORD); - effectiveCookie = password ?? await DaoSecuredString.get(_COOKIE); + effectiveCookie = await DaoSecuredString.get(_COOKIE); } on PlatformException { /// Decrypting the values can go wrong if, for example, the app was /// manually overwritten from an external apk. @@ -55,9 +57,7 @@ class UserManagementProvider with ChangeNotifier { Logs.e('Credentials query failed, you have been logged out'); } - if (effectiveUserId == null || - effectivePassword == null || - effectiveCookie == null) { + if (effectiveUserId == null || effectivePassword == null) { return; } @@ -112,14 +112,16 @@ class UserManagementProvider with ChangeNotifier { password: user.password, ), ); - switch (loginResult.type) { - case LoginResultType.successful: - case LoginResultType.serverIssue: - case LoginResultType.exception: - return; - case LoginResultType.unsuccessful: - // TODO(m123): Notify the user - await logout(); + + if (loginResult.type == LoginResultType.unsuccessful) { + // TODO(m123): Notify the user + await logout(); + return; + } + + /// Save the cookie if necessary + if (user.cookie == null && loginResult.user?.cookie != null) { + putUser(loginResult.user!); } } } diff --git a/packages/smooth_app/lib/database/dao_osm_location.dart b/packages/smooth_app/lib/database/dao_osm_location.dart new file mode 100644 index 00000000000..b02e041f70c --- /dev/null +++ b/packages/smooth_app/lib/database/dao_osm_location.dart @@ -0,0 +1,127 @@ +import 'dart:async'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/database/abstract_sql_dao.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:sqflite/sqflite.dart'; + +/// DAO about OSM locations. +class DaoOsmLocation extends AbstractSqlDao { + DaoOsmLocation(super.localDatabase); + + static const String _table = 'osm_location'; + static const String _columnId = 'osm_id'; + static const String _columnType = 'osm_type'; + static const String _columnLongitude = 'longitude'; + static const String _columnLatitude = 'latitude'; + static const String _columnName = 'name'; + static const String _columnStreet = 'street'; + static const String _columnCity = 'city'; + static const String _columnPostCode = 'post_code'; + static const String _columnCountry = 'country'; + static const String _columnCountryCode = 'country_code'; + static const String _columnLastAccess = 'last_access'; + + static const List _columns = [ + _columnId, + _columnType, + _columnLongitude, + _columnLatitude, + _columnName, + _columnStreet, + _columnCity, + _columnPostCode, + _columnCountry, + _columnCountryCode, + _columnLastAccess, + ]; + + static FutureOr onUpgrade( + final Database db, + final int oldVersion, + final int newVersion, + ) async { + if (oldVersion < 6) { + await db.execute('create table $_table(' + ' $_columnId INT NOT NULL' + ',$_columnType TEXT NOT NULL' + ',$_columnLongitude REAL NOT NULL' + ',$_columnLatitude REAL NOT NULL' + ',$_columnName TEXT' + ',$_columnStreet TEXT' + ',$_columnCity TEXT' + ',$_columnPostCode TEXT' + ',$_columnCountry TEXT' + ',$_columnCountryCode TEXT' + ',$_columnLastAccess INT NOT NULL' + // cf. https://www.sqlite.org/lang_conflict.html + ',PRIMARY KEY($_columnId,$_columnType) on conflict replace' + ')'); + } + } + + /// Deletes the [OsmLocation] that matches the key. + Future delete(final OsmLocation osmLocation) async => + localDatabase.database.delete( + _table, + where: '$_columnId = ? AND $_columnType = ?', + whereArgs: [osmLocation.osmId, osmLocation.osmType.offTag], + ); + + /// Returns all the [OsmLocation]s, ordered by descending last access. + Future> getAll() async { + final List result = []; + final List> queryResults = + await localDatabase.database.query( + _table, + columns: _columns, + orderBy: '$_columnLastAccess DESC', + ); + for (final Map row in queryResults) { + final OsmLocation? item = _getItemFromQueryResult(row); + if (item != null) { + result.add(item); + } + } + return result; + } + + Future put(final OsmLocation osmLocation) async => + localDatabase.database.insert( + _table, + { + _columnId: osmLocation.osmId, + _columnType: osmLocation.osmType.offTag, + _columnLongitude: osmLocation.longitude, + _columnLatitude: osmLocation.latitude, + _columnName: osmLocation.name, + _columnStreet: osmLocation.street, + _columnCity: osmLocation.city, + _columnPostCode: osmLocation.postcode, + _columnCountry: osmLocation.country, + _columnCountryCode: osmLocation.countryCode, + _columnLastAccess: LocalDatabase.nowInMillis(), + }, + ); + + OsmLocation? _getItemFromQueryResult(final Map row) { + final LocationOSMType? type = + LocationOSMType.fromOffTag(row[_columnType] as String); + if (type == null) { + // very very unlikely + return null; + } + return OsmLocation( + osmId: row[_columnId] as int, + osmType: type, + longitude: row[_columnLongitude] as double, + latitude: row[_columnLatitude] as double, + name: row[_columnName] as String?, + street: row[_columnStreet] as String?, + city: row[_columnCity] as String?, + postcode: row[_columnPostCode] as String?, + country: row[_columnCountry] as String?, + countryCode: row[_columnCountryCode] as String?, + ); + } +} diff --git a/packages/smooth_app/lib/database/dao_string_list.dart b/packages/smooth_app/lib/database/dao_string_list.dart index 568e48d8790..ff42487464f 100644 --- a/packages/smooth_app/lib/database/dao_string_list.dart +++ b/packages/smooth_app/lib/database/dao_string_list.dart @@ -8,7 +8,11 @@ class DaoStringList extends AbstractDao { static const String _hiveBoxName = 'stringList'; - static const String keySearchHistory = 'searchHistory'; + /// Key for the list of product search history. + static const String keySearchProductHistory = 'searchHistory'; + + /// Key for the list of location search history + static const String keySearchLocationHistory = 'searchLocationHistory'; /// Key for the list of task ids. static const String keyTasks = 'tasks'; @@ -18,7 +22,8 @@ class DaoStringList extends AbstractDao { /// Max lengths of each key (null means no limit). static const Map _maxLengths = { - keySearchHistory: 10, + keySearchProductHistory: 10, + keySearchLocationHistory: 10, keyTasks: null, // TODO(monsieurtanuki): more "latest" languages are possible if we create a page to remove some of them keyLanguages: 1, diff --git a/packages/smooth_app/lib/database/local_database.dart b/packages/smooth_app/lib/database/local_database.dart index 5df0729e0b3..b3542785dee 100644 --- a/packages/smooth_app/lib/database/local_database.dart +++ b/packages/smooth_app/lib/database/local_database.dart @@ -12,6 +12,7 @@ import 'package:smooth_app/database/abstract_dao.dart'; import 'package:smooth_app/database/dao_hive_product.dart'; import 'package:smooth_app/database/dao_instant_string.dart'; import 'package:smooth_app/database/dao_int.dart'; +import 'package:smooth_app/database/dao_osm_location.dart'; import 'package:smooth_app/database/dao_product.dart'; import 'package:smooth_app/database/dao_product_last_access.dart'; import 'package:smooth_app/database/dao_product_list.dart'; @@ -20,7 +21,7 @@ import 'package:smooth_app/database/dao_string_list.dart'; import 'package:smooth_app/database/dao_string_list_map.dart'; import 'package:smooth_app/database/dao_transient_operation.dart'; import 'package:smooth_app/database/dao_work_barcode.dart'; -import 'package:sqflite/sqflite.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; class LocalDatabase extends ChangeNotifier { LocalDatabase._(final Database database) : _database = database { @@ -52,18 +53,22 @@ class LocalDatabase extends ChangeNotifier { static Future getLocalDatabase() async { // sql from there - final String databasesRootPath; + String? databasesRootPath; if (defaultTargetPlatform == TargetPlatform.iOS) { // as suggested in https://pub.dev/documentation/sqflite/latest/sqflite/getDatabasesPath.html final Directory directory = await getLibraryDirectory(); databasesRootPath = directory.path; - } else { - databasesRootPath = await getDatabasesPath(); + } else if (Platform.isLinux || Platform.isWindows) { + sqfliteFfiInit(); + databaseFactory = databaseFactoryFfi; } + + databasesRootPath ??= await getDatabasesPath(); + final String databasePath = join(databasesRootPath, 'smoothie.db'); final Database database = await openDatabase( databasePath, - version: 5, + version: 6, singleInstance: true, onUpgrade: _onUpgrade, ); @@ -107,5 +112,6 @@ class LocalDatabase extends ChangeNotifier { await DaoProduct.onUpgrade(db, oldVersion, newVersion); await DaoWorkBarcode.onUpgrade(db, oldVersion, newVersion); await DaoProductLastAccess.onUpgrade(db, oldVersion, newVersion); + await DaoOsmLocation.onUpgrade(db, oldVersion, newVersion); } } diff --git a/packages/smooth_app/lib/generic_lib/buttons/smooth_simple_button.dart b/packages/smooth_app/lib/generic_lib/buttons/smooth_simple_button.dart index cc978a528ab..52dae9e156d 100644 --- a/packages/smooth_app/lib/generic_lib/buttons/smooth_simple_button.dart +++ b/packages/smooth_app/lib/generic_lib/buttons/smooth_simple_button.dart @@ -33,19 +33,19 @@ class SmoothSimpleButton extends StatelessWidget { style: ButtonStyle( backgroundColor: buttonColor == null ? null - : MaterialStateProperty.all(buttonColor!), - shape: MaterialStateProperty.all( + : WidgetStateProperty.all(buttonColor!), + shape: WidgetStateProperty.all( RoundedRectangleBorder(borderRadius: borderRadius), ), overlayColor: context.read().isAmoledTheme - ? MaterialStateProperty.resolveWith((Set states) { - return states.contains(MaterialState.pressed) + ? WidgetStateProperty.resolveWith((Set states) { + return states.contains(WidgetState.pressed) ? Theme.of(context).colorScheme.primary.withOpacity(0.3) : null; }) : null, side: context.read().isAmoledTheme - ? MaterialStateProperty.all( + ? WidgetStateProperty.all( const BorderSide(color: Colors.white), ) : null, diff --git a/packages/smooth_app/lib/generic_lib/dialogs/smooth_alert_dialog.dart b/packages/smooth_app/lib/generic_lib/dialogs/smooth_alert_dialog.dart index f9f51c0aed0..0d03fca3a89 100644 --- a/packages/smooth_app/lib/generic_lib/dialogs/smooth_alert_dialog.dart +++ b/packages/smooth_app/lib/generic_lib/dialogs/smooth_alert_dialog.dart @@ -26,6 +26,7 @@ import 'package:smooth_app/helpers/keyboard_helper.dart'; class SmoothAlertDialog extends StatelessWidget { const SmoothAlertDialog({ this.title, + this.leadingTitle, required this.body, this.positiveAction, this.negativeAction, @@ -41,6 +42,7 @@ class SmoothAlertDialog extends StatelessWidget { ); final String? title; + final Widget? leadingTitle; final bool close; final Widget body; final SmoothActionButton? positiveAction; @@ -143,7 +145,12 @@ class SmoothAlertDialog extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - if (title != null) _SmoothDialogTitle(label: title!, close: close), + if (title != null) + _SmoothDialogTitle( + label: title!, + close: close, + leading: leadingTitle, + ), body, ], ), @@ -158,15 +165,20 @@ class _SmoothDialogTitle extends StatelessWidget { const _SmoothDialogTitle({ required this.label, required this.close, + this.leading, }); static const double _titleHeight = 32.0; final String label; final bool close; + final Widget? leading; @override Widget build(BuildContext context) { + final TextStyle textStyle = + Theme.of(context).textTheme.displayMedium ?? const TextStyle(); + return Column( mainAxisSize: MainAxisSize.min, children: [ @@ -176,12 +188,24 @@ class _SmoothDialogTitle extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ + if (leading != null) + Padding( + padding: EdgeInsetsDirectional.only( + top: leading is Icon ? 2.0 : 0.0, + end: SMALL_SPACE, + ), + child: IconTheme( + data: IconThemeData( + color: textStyle.color, + ), + child: leading!), + ), _buildCross(true), Expanded( child: FittedBox( child: Text( label, - style: Theme.of(context).textTheme.displayMedium, + style: textStyle, ), ), ), @@ -189,7 +213,7 @@ class _SmoothDialogTitle extends StatelessWidget { ], ), ), - Divider(color: Theme.of(context).colorScheme.onBackground), + Divider(color: Theme.of(context).colorScheme.onSurface), const SizedBox(height: 12), ], ); @@ -221,16 +245,21 @@ class _SmoothDialogCrossButton extends StatelessWidget { maintainAnimation: true, maintainState: true, visible: visible, - child: InkWell( - customBorder: const CircleBorder(), - child: const Padding( - padding: EdgeInsets.all(SMALL_SPACE), - child: Icon( - Icons.close, - size: _SmoothDialogTitle._titleHeight - (2 * SMALL_SPACE), + child: Semantics( + label: MaterialLocalizations.of(context).closeButtonLabel, + button: true, + excludeSemantics: true, + child: InkWell( + customBorder: const CircleBorder(), + child: const Padding( + padding: EdgeInsets.all(SMALL_SPACE), + child: Icon( + Icons.close, + size: _SmoothDialogTitle._titleHeight - (2 * SMALL_SPACE), + ), ), + onTap: () => Navigator.of(context, rootNavigator: true).pop(), ), - onTap: () => Navigator.of(context, rootNavigator: true).pop(), ), ); } else { @@ -670,9 +699,9 @@ class SmoothListAlertDialog extends StatelessWidget { vertical: SMALL_SPACE, ), body: SizedBox( - height: MediaQuery.of(context).size.height / + height: MediaQuery.sizeOf(context).height / (context.keyboardVisible ? 1.0 : 1.5), - width: MediaQuery.of(context).size.width, + width: MediaQuery.sizeOf(context).width, child: Column( children: [ Container( diff --git a/packages/smooth_app/lib/generic_lib/svg_icon_chip.dart b/packages/smooth_app/lib/generic_lib/svg_icon_chip.dart index 0c0af7d78b2..b1081c96757 100644 --- a/packages/smooth_app/lib/generic_lib/svg_icon_chip.dart +++ b/packages/smooth_app/lib/generic_lib/svg_icon_chip.dart @@ -11,7 +11,7 @@ class SvgIconChip extends StatelessWidget { final double height; @override - Widget build(BuildContext context) => Container( + Widget build(BuildContext context) => ConstrainedBox( constraints: BoxConstraints(minWidth: height), child: SvgCache(iconUrl, height: height), ); diff --git a/packages/smooth_app/lib/generic_lib/widgets/images/smooth_image.dart b/packages/smooth_app/lib/generic_lib/widgets/images/smooth_image.dart index 3e8a6be7cf1..95e24525693 100644 --- a/packages/smooth_app/lib/generic_lib/widgets/images/smooth_image.dart +++ b/packages/smooth_app/lib/generic_lib/widgets/images/smooth_image.dart @@ -18,7 +18,16 @@ class SmoothImage extends StatelessWidget { this.fit = BoxFit.cover, this.rounded = true, this.heroTag, - }); + this.cacheWidth, + this.cacheHeight, + }) : assert( + cacheWidth == null || imageProvider is NetworkImage, + 'cacheWidth requires a NetworkImage', + ), + assert( + cacheHeight == null || imageProvider is NetworkImage, + 'cacheHeight requires a NetworkImage', + ); final ImageProvider? imageProvider; final double? height; @@ -28,17 +37,28 @@ class SmoothImage extends StatelessWidget { final BoxFit fit; final String? heroTag; final bool rounded; + final int? cacheWidth; + final int? cacheHeight; @override Widget build(BuildContext context) { - Widget child = imageProvider == null - ? const PictureNotFound() - : Image( - image: imageProvider!, - fit: fit, - loadingBuilder: _loadingBuilder, - errorBuilder: _errorBuilder, - ); + Widget child = switch (imageProvider) { + NetworkImage(url: final String url) => Image.network( + url, + fit: fit, + loadingBuilder: _loadingBuilder, + errorBuilder: _errorBuilder, + cacheWidth: cacheWidth, + cacheHeight: cacheHeight, + ), + ImageProvider() => Image( + image: imageProvider!, + fit: fit, + loadingBuilder: _loadingBuilder, + errorBuilder: _errorBuilder, + ), + _ => const PictureNotFound(), + }; if (heroTag != null) { child = Hero(tag: heroTag!, child: child); diff --git a/packages/smooth_app/lib/generic_lib/widgets/smooth_back_button.dart b/packages/smooth_app/lib/generic_lib/widgets/smooth_back_button.dart index 038dc1359e6..ca4e99636d1 100644 --- a/packages/smooth_app/lib/generic_lib/widgets/smooth_back_button.dart +++ b/packages/smooth_app/lib/generic_lib/widgets/smooth_back_button.dart @@ -16,13 +16,21 @@ class SmoothBackButton extends StatelessWidget { final Color? iconColor; @override - Widget build(BuildContext context) => Material( - type: MaterialType.transparency, + Widget build(BuildContext context) { + final MaterialLocalizations localizations = + MaterialLocalizations.of(context); + + return Material( + type: MaterialType.transparency, + child: Semantics( + value: localizations.backButtonTooltip, + button: true, + excludeSemantics: true, child: InkWell( onTap: onPressed ?? () => Navigator.maybePop(context), customBorder: const CircleBorder(), child: Tooltip( - message: MaterialLocalizations.of(context).backButtonTooltip, + message: localizations.backButtonTooltip, child: Padding( padding: _iconPadding, child: Icon( @@ -36,7 +44,9 @@ class SmoothBackButton extends StatelessWidget { ), ), ), - ); + ), + ); + } /// The iOS/macOS icon requires a little padding to be well-centered EdgeInsetsGeometry get _iconPadding { diff --git a/packages/smooth_app/lib/generic_lib/widgets/smooth_gauge.dart b/packages/smooth_app/lib/generic_lib/widgets/smooth_gauge.dart deleted file mode 100644 index 95d6643461c..00000000000 --- a/packages/smooth_app/lib/generic_lib/widgets/smooth_gauge.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:percent_indicator/percent_indicator.dart'; - -class SmoothGauge extends StatelessWidget { - const SmoothGauge({ - required this.value, - this.size = 80.0, - required this.color, - this.backgroundColor, - this.circular = true, - this.width = 100.0, - }); - - final double value; - final double size; - final Color color; - final Color? backgroundColor; - final bool circular; - final double width; - - @override - Widget build(BuildContext context) { - return circular - ? CircularPercentIndicator( - radius: size, - lineWidth: 5.0, - percent: value <= 1.0 ? value : 1.0, - center: Text( - '${(value * 100).floor()}%', - style: TextStyle(color: color), - ), - progressColor: color, - backgroundColor: backgroundColor ?? color.withAlpha(50), - circularStrokeCap: CircularStrokeCap.round, - ) - : LinearPercentIndicator( - width: width, - percent: value <= 1.0 ? value : 1.0, - progressColor: color, - backgroundColor: backgroundColor ?? color.withAlpha(50), - ); - } -} diff --git a/packages/smooth_app/lib/generic_lib/widgets/smooth_text_form_field.dart b/packages/smooth_app/lib/generic_lib/widgets/smooth_text_form_field.dart index 8ea02fadb2a..ce4d0fc6166 100644 --- a/packages/smooth_app/lib/generic_lib/widgets/smooth_text_form_field.dart +++ b/packages/smooth_app/lib/generic_lib/widgets/smooth_text_form_field.dart @@ -24,6 +24,7 @@ class SmoothTextFormField extends StatefulWidget { this.onFieldSubmitted, this.autofocus, this.focusNode, + this.spellCheckConfiguration, }); final TextFieldTypes type; @@ -40,6 +41,7 @@ class SmoothTextFormField extends StatefulWidget { final ValueChanged? onFieldSubmitted; final bool? autofocus; final FocusNode? focusNode; + final SpellCheckConfiguration? spellCheckConfiguration; @override State createState() => _SmoothTextFormFieldState(); @@ -83,6 +85,8 @@ class _SmoothTextFormFieldState extends State { setState(() {}); } }, + spellCheckConfiguration: widget.spellCheckConfiguration ?? + const SpellCheckConfiguration.disabled(), onFieldSubmitted: widget.onFieldSubmitted, style: TextStyle(fontSize: textSize), cursorHeight: textSize * (textStyle.height ?? 1.4), diff --git a/packages/smooth_app/lib/helpers/launch_url_helper.dart b/packages/smooth_app/lib/helpers/launch_url_helper.dart index 95a8aff0bcc..3e1264d2f16 100644 --- a/packages/smooth_app/lib/helpers/launch_url_helper.dart +++ b/packages/smooth_app/lib/helpers/launch_url_helper.dart @@ -1,10 +1,26 @@ import 'dart:io'; +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:url_launcher/url_launcher.dart'; class LaunchUrlHelper { - LaunchUrlHelper._(); + const LaunchUrlHelper._(); + + static Future launchURLAndFollowDeepLinks( + BuildContext context, + String url, + ) async { + assert(url.isNotEmpty); + + if (url.startsWith(RegExp('http(s)?://[a-z]*.openfoodfacts.(net|org)'))) { + AnalyticsHelper.trackOutlink(url: url); + GoRouter.of(context).go(url); + } else { + return launchURL(url); + } + } /// Launches the url in an external browser. static Future launchURL(String url) async { diff --git a/packages/smooth_app/lib/helpers/num_utils.dart b/packages/smooth_app/lib/helpers/num_utils.dart new file mode 100644 index 00000000000..e9dc242b7d4 --- /dev/null +++ b/packages/smooth_app/lib/helpers/num_utils.dart @@ -0,0 +1,21 @@ +extension DoubleExtension on double { + /// Returns the progress of the current value between [min] and [max]. + /// /!\ May return < 0 or > 1 values, use [progressAndClamp] if you want to limit the output. + /// Eg: 5.progress(0, 10) => 0.5 + /// Eg: -1.progress(0, 5) => -0.2 + double progress(num min, num max) { + return (this - min) / (max - min); + } + + /// Returns the progress of the current value between [min] and [max]. + /// The minimum value will always be 0 and the maximum value will always be [clamp]. + double progressAndClamp(num min, double max, double clamp) { + return progress(min, max).clamp(0.0, clamp); + } +} + +extension IntExtension on int { + double progress(int min, int max) { + return (this - min) / (max - min); + } +} diff --git a/packages/smooth_app/lib/helpers/physics.dart b/packages/smooth_app/lib/helpers/physics.dart new file mode 100644 index 00000000000..8a3b0e30bed --- /dev/null +++ b/packages/smooth_app/lib/helpers/physics.dart @@ -0,0 +1,429 @@ +import 'dart:io'; +import 'dart:math' as math; + +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; + +class VerticalClampScroll extends StatefulWidget { + const VerticalClampScroll({ + required this.steps, + required this.child, + super.key, + }) : assert(steps.length >= 2); + + final List steps; + final Widget child; + + @override + State createState() => _VerticalClampScrollState(); +} + +class _VerticalClampScrollState extends State { + late final Iterable _reversedSteps; + final VerticalClampScrollLimiter _limiter = VerticalClampScrollLimiter(); + ScrollDirection? _direction; + ScrollMetrics? _startMetrics; + + @override + void initState() { + super.initState(); + _reversedSteps = widget.steps.reversed; + } + + @override + Widget build(BuildContext context) { + return ScrollConfiguration( + behavior: _CustomScrollBehavior( + VerticalSnapScrollPhysics.get( + steps: widget.steps, + ), + ), + child: NotificationListener( + onNotification: (ScrollNotification notif) { + if (notif.metrics.axisDirection == AxisDirection.left || + notif.metrics.axisDirection == AxisDirection.right) { + return false; + } + + if (notif is UserScrollNotification) { + _direction = notif.direction; + + if (notif.direction != ScrollDirection.idle) { + _startMetrics = notif.metrics; + } + } else if (notif is ScrollUpdateNotification) { + _onScrollUpdate(notif); + } else if (notif is ScrollEndNotification) { + _onScrollEnd(notif); + } + + return true; + }, + child: ChangeNotifierProvider( + create: (_) => _limiter, + child: widget.child, + ), + ), + ); + } + + void _onScrollEnd(ScrollEndNotification notif) { + if (notif.dragDetails != null) { + final (double? min, double? max) = _getRange( + widget.steps, + notif.metrics.pixels, + ); + + double? scrollTo; + // Down + if (_direction == ScrollDirection.reverse && max != null) { + scrollTo = max; + } else if (_direction == ScrollDirection.forward && min != null) { + scrollTo = min; + } + + if (scrollTo != null) { + Future.delayed(Duration.zero, () { + context.read().animateTo( + scrollTo!, + curve: Curves.easeOutCubic, + duration: const Duration(milliseconds: 500), + ); + }); + } + } + } + + void _onScrollUpdate(ScrollUpdateNotification notif) { + if (_direction != ScrollDirection.forward || _startMetrics == null) { + return; + } + + if (_limiter.value != null && notif.metrics.pixels > _limiter.value!) { + context.read().position.correctPixels(_limiter.value!); + return; + } + + for (int i = 0; i != _reversedSteps.length; i++) { + if (_blockScrollIfNecessary( + notif, _reversedSteps.elementAt(i), i == _reversedSteps.length - 1)) { + break; + } + } + } + + bool _blockScrollIfNecessary( + ScrollUpdateNotification notif, + double step, + bool isLast, + ) { + if (isLast) { + if (_startMetrics!.extentBefore >= step) { + if (notif.metrics.pixels <= step) { + context.read().position.correctPixels(step); + } + return true; + } + } + + return false; + } +} + +class VerticalClampScrollLimiter extends ValueNotifier { + VerticalClampScrollLimiter() : super(null); + + void limitScroll(double height) { + value = height; + } +} + +class VerticalSnapScrollPhysics extends ScrollPhysics { + static ScrollPhysics get({ + required List steps, + bool lastStepBlocking = true, + }) { + if (Platform.isIOS || Platform.isMacOS) { + return _VerticalSnapBouncingScrollPhysics( + steps: steps, + lastStepBlocking: lastStepBlocking, + ); + } else { + return _VerticalSnapClampingScrollPhysics( + steps: steps, + lastStepBlocking: lastStepBlocking, + ); + } + } +} + +//ignore: must_be_immutable +class _VerticalSnapClampingScrollPhysics extends ClampingScrollPhysics + with _VerticalSnapScrollPhysicsHelper { + _VerticalSnapClampingScrollPhysics({ + required List steps, + bool lastStepBlocking = true, + }) { + _init( + steps: steps, + lastStepBlocking: lastStepBlocking, + ); + } + + @override + _VerticalSnapClampingScrollPhysics applyTo(ScrollPhysics? ancestor) { + return _VerticalSnapClampingScrollPhysics( + steps: _steps, + lastStepBlocking: _lastStepBlocking, + ); + } + + @override + Simulation? createBallisticSimulation( + ScrollMetrics position, + double velocity, + ) { + return createBallisticSimulation2(position, velocity); + } +} + +//ignore: must_be_immutable +class _VerticalSnapBouncingScrollPhysics extends BouncingScrollPhysics + with _VerticalSnapScrollPhysicsHelper { + _VerticalSnapBouncingScrollPhysics({ + required List steps, + bool lastStepBlocking = true, + }) { + _init( + steps: steps, + lastStepBlocking: lastStepBlocking, + ); + } + + @override + _VerticalSnapBouncingScrollPhysics applyTo(ScrollPhysics? ancestor) { + return _VerticalSnapBouncingScrollPhysics( + steps: _steps, + lastStepBlocking: _lastStepBlocking, + ); + } + + @override + Simulation? createBallisticSimulation( + ScrollMetrics position, + double velocity, + ) { + return createBallisticSimulation2(position, velocity); + } +} + +/// A custom [ScrollPhysics] that snaps to specific [steps]. +/// ignore: must_be_immutable +mixin _VerticalSnapScrollPhysicsHelper on ScrollPhysics { + void _init({ + required List steps, + bool lastStepBlocking = true, + }) { + _steps = steps.toList()..sort(); + _lastStepBlocking = lastStepBlocking; + _ignoreNextScroll = false; + } + + late List _steps; + + // If true, scrolling from the bottom with be blocked at the last step + // If false, scrolling from the bottom will continue + late bool _lastStepBlocking; + late bool _ignoreNextScroll; + + double? _lastPixels; + + Simulation? createBallisticSimulation2( + ScrollMetrics position, + double velocity, + ) { + final Tolerance tolerance = toleranceFor(position); + if (velocity.abs() < tolerance.velocity) { + return null; + } + if (velocity > 0.0 && position.pixels >= position.maxScrollExtent) { + _ignoreNextScroll = false; + return null; + } + if (velocity < 0.0 && position.pixels <= position.minScrollExtent) { + _ignoreNextScroll = false; + return null; + } + + final Simulation? simulation = + super.createBallisticSimulation(position, velocity); + double? proposedPixels = simulation?.x(double.infinity); + + if (simulation == null || proposedPixels == null) { + final (double? min, _) = _getRange(_steps, position.pixels); + + if (min != null && min != _steps.last && _ignoreNextScroll) { + return ScrollSpringSimulation( + spring, + position.pixels, + min, + velocity, + tolerance: toleranceFor(position), + ); + } else { + _ignoreNextScroll = false; + return null; + } + } + + _ignoreNextScroll = false; + final (double? min, double? max) = _getRange(_steps, position.pixels); + bool hasChanged = false; + if (min != null && max == null) { + if (proposedPixels < min) { + proposedPixels = min; + hasChanged = true; + } + } else if (min != null && max != null) { + if (position.pixels - proposedPixels > 0) { + proposedPixels = min; + } else { + proposedPixels = max; + } + hasChanged = true; + } + + if (_lastPixels == null) { + _lastPixels = proposedPixels; + } else { + _lastPixels = _fixInconsistency(proposedPixels); + } + + /// Smooth scroll to a step + if (hasChanged && (_lastStepBlocking || position.pixels < _steps.last)) { + return ScrollSpringSimulation( + spring, + position.pixels, + _lastPixels!, + velocity, + tolerance: tolerance, + ); + } + + /// Normal scrolling + return simulation; + } + + // In some cases, the proposed pixels have a giant space and finding the range + // is incorrect. In that case, we ensure to have a contiguous range. + double _fixInconsistency(double proposedPixels) { + return fixInconsistency(_steps, proposedPixels, _lastPixels!); + } + + static double fixInconsistency( + List steps, + double proposedPixels, + double initialPixelPosition, + ) { + final int newPosition = _getStepPosition(steps, proposedPixels); + final int oldPosition = _getStepPosition(steps, initialPixelPosition); + + if (newPosition - oldPosition >= 2) { + return steps[math.min(newPosition - 1, 0)]; + } else if (newPosition - oldPosition <= -2) { + return steps[math.min(newPosition + 1, steps.length - 1)]; + } + + return proposedPixels; + } + + static int _getStepPosition(List steps, double pixels) { + for (int i = steps.length - 1; i >= 0; i--) { + final double step = steps.elementAt(i); + + if (pixels >= step) { + return i; + } + } + + return 0; + } +} + +(double?, double?) _getRange(List steps, double position) { + for (int i = steps.length - 1; i >= 0; i--) { + final double step = steps[i]; + + if (i == steps.length - 1 && position > step) { + return (step, null); + } else if (position > step && position < steps[i + 1]) { + return (step, steps[i + 1]); + } + } + + return (null, null); +} + +class _CustomScrollBehavior extends ScrollBehavior { + const _CustomScrollBehavior(this.physics); + + final ScrollPhysics physics; + + @override + ScrollPhysics getScrollPhysics(BuildContext context) => physics; +} + +class HorizontalSnapScrollPhysics extends ScrollPhysics { + const HorizontalSnapScrollPhysics({super.parent, required this.snapSize}); + + final double snapSize; + + @override + HorizontalSnapScrollPhysics applyTo(ScrollPhysics? ancestor) { + return HorizontalSnapScrollPhysics( + parent: buildParent(ancestor), snapSize: snapSize); + } + + double _getPage(ScrollMetrics position) { + return position.pixels / snapSize; + } + + double _getPixels(ScrollMetrics position, double page) { + return page * snapSize; + } + + double _getTargetPixels( + ScrollMetrics position, Tolerance tolerance, double velocity) { + double page = _getPage(position); + if (velocity < -tolerance.velocity) { + page -= 0.5; + } else if (velocity > tolerance.velocity) { + page += 0.5; + } + return _getPixels(position, page.roundToDouble()); + } + + @override + Simulation? createBallisticSimulation( + ScrollMetrics position, + double velocity, + ) { + // If we're out of range and not headed back in range, defer to the parent + // ballistics, which should put us back in range at a page boundary. + if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) || + (velocity >= 0.0 && position.pixels >= position.maxScrollExtent)) { + return super.createBallisticSimulation(position, velocity); + } + final Tolerance tolerance = toleranceFor(position); + final double target = _getTargetPixels(position, tolerance, velocity); + if (target != position.pixels) { + return ScrollSpringSimulation(spring, position.pixels, target, velocity, + tolerance: tolerance); + } + return null; + } + + @override + bool get allowImplicitScrolling => false; +} diff --git a/packages/smooth_app/lib/helpers/product_cards_helper.dart b/packages/smooth_app/lib/helpers/product_cards_helper.dart index b26a228a7fe..cf8553433f3 100644 --- a/packages/smooth_app/lib/helpers/product_cards_helper.dart +++ b/packages/smooth_app/lib/helpers/product_cards_helper.dart @@ -270,6 +270,7 @@ ProductImageData getProductImageData( imageUrl: productImage.getUrl( product.barcode!, imageSize: ImageSize.DISPLAY, + uriHelper: ProductQuery.uriProductHelper, ), language: language, ); diff --git a/packages/smooth_app/lib/helpers/provider_helper.dart b/packages/smooth_app/lib/helpers/provider_helper.dart index 9af48309ec6..3e5753cc5a4 100644 --- a/packages/smooth_app/lib/helpers/provider_helper.dart +++ b/packages/smooth_app/lib/helpers/provider_helper.dart @@ -38,3 +38,59 @@ class _ListenerState extends SingleChildState> { return child ?? const SizedBox.shrink(); } } + +/// Same as [Consumer] but only rebuilds if [buildWhen] returns true +/// (And on the first build) +class ConsumerFilter extends StatefulWidget { + const ConsumerFilter({ + required this.builder, + required this.buildWhen, + this.child, + super.key, + }); + + final Widget Function( + BuildContext context, + T value, + Widget? child, + ) builder; + final bool Function(T? previousValue, T currentValue) buildWhen; + + final Widget? child; + + @override + State> createState() => _ConsumerFilterState(); +} + +class _ConsumerFilterState extends State> { + T? oldValue; + Widget? oldWidget; + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (BuildContext context, T value, Widget? child) { + if (widget.buildWhen(oldValue, value) || oldWidget == null) { + oldWidget = widget.builder( + context, + value, + child, + ); + } + + oldValue = value; + + return widget.builder( + context, + value, + oldWidget, + ); + }, + child: widget.child, + ); + } +} + +extension ValueNotifierExtensions on ValueNotifier { + void emit(T value) => this.value = value; +} diff --git a/packages/smooth_app/lib/helpers/strings_helper.dart b/packages/smooth_app/lib/helpers/strings_helper.dart index 25bfa53d969..055db2cb4db 100644 --- a/packages/smooth_app/lib/helpers/strings_helper.dart +++ b/packages/smooth_app/lib/helpers/strings_helper.dart @@ -1,3 +1,5 @@ +import 'package:flutter/material.dart'; + extension StringExtensions on String { /// Returns a list containing all positions of a [charCode] /// By default, the case is taken into account. @@ -42,3 +44,97 @@ extension StringExtensions on String { }); } } + +class TextHelper { + const TextHelper._(); + + /// Split the text into parts. + /// Eg: with the symbol '*' + /// 'Hello *world*!' => [('Hello ', defaultStyle), ('world', highlightedStyle), ('!', defaultStyle)] + static List<(String, TextStyle?)> getPartsBetweenSymbol({ + required String text, + required String symbol, + required int symbolLength, + required TextStyle? defaultStyle, + required TextStyle? highlightedStyle, + }) { + text = text.replaceAll(r'\n', '\n'); + + final Iterable highlightedParts = + RegExp('$symbol[^$symbol]+$symbol').allMatches(text); + + final List<(String, TextStyle?)> parts = <(String, TextStyle?)>[]; + + if (highlightedParts.isEmpty) { + parts.add((text, defaultStyle)); + } else { + parts + .add((text.substring(0, highlightedParts.first.start), defaultStyle)); + for (int i = 0; i != highlightedParts.length; i++) { + final RegExpMatch subPart = highlightedParts.elementAt(i); + + parts.add( + ( + text.substring( + subPart.start + symbolLength, subPart.end - symbolLength), + highlightedStyle + ), + ); + + if (i < highlightedParts.length - 1) { + parts.add(( + text.substring( + subPart.end, highlightedParts.elementAt(i + 1).start), + defaultStyle + )); + } else if (subPart.end < text.length) { + parts.add((text.substring(subPart.end, text.length), defaultStyle)); + } + } + } + return parts; + } +} + +class FormattedText extends StatelessWidget { + const FormattedText({ + required this.text, + this.textStyle, + this.textAlign, + }); + + final String text; + final TextStyle? textStyle; + final TextAlign? textAlign; + + @override + Widget build(BuildContext context) { + final TextStyle defaultTextStyle = textStyle ?? const TextStyle(); + return Semantics( + value: text.replaceAll(r'**', '').replaceAll('\n', ' '), + excludeSemantics: true, + child: RichText( + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: TextHelper.getPartsBetweenSymbol( + text: text, + symbol: r'\*\*', + symbolLength: 2, + defaultStyle: defaultTextStyle, + highlightedStyle: + const TextStyle(fontWeight: FontWeight.bold)) + .map( + ((String, TextStyle?) part) { + return TextSpan( + text: part.$1, + style: defaultTextStyle.merge(part.$2), + semanticsLabel: '-', + ); + }, + ).toList(growable: false), + ), + textAlign: textAlign ?? TextAlign.start, + ), + ); + } +} diff --git a/packages/smooth_app/lib/helpers/ui_helpers.dart b/packages/smooth_app/lib/helpers/ui_helpers.dart index 0f392b67d36..ca03682d7fb 100644 --- a/packages/smooth_app/lib/helpers/ui_helpers.dart +++ b/packages/smooth_app/lib/helpers/ui_helpers.dart @@ -16,7 +16,7 @@ class IconWidgetSizer { static const double _ICON_WIDGET_SIZE_RATIO = 1 / 10; static double getIconSizeFromContext(BuildContext context) { - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); return screenSize.width * _ICON_WIDGET_SIZE_RATIO; } diff --git a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_action_card.dart b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_action_card.dart index 55fcc1888ad..61c09bbaaad 100644 --- a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_action_card.dart +++ b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_action_card.dart @@ -31,7 +31,7 @@ class KnowledgePanelActionCard extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SmoothHtmlWidget(element.html), + if (element.html != null) SmoothHtmlWidget(element.html!), const SizedBox(height: SMALL_SPACE), ...actionWidgets, ], diff --git a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_group_card.dart b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_group_card.dart index 0ac2b7f6603..6c9ef63db4f 100644 --- a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_group_card.dart +++ b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_group_card.dart @@ -28,10 +28,13 @@ class KnowledgePanelGroupCard extends StatelessWidget { if (groupElement.title != null && groupElement.title!.isNotEmpty) Padding( padding: const EdgeInsetsDirectional.only(top: LARGE_SPACE), - child: Text( - groupElement.title!, - style: - themeData.textTheme.titleSmall!.apply(color: Colors.grey), + child: Semantics( + explicitChildNodes: true, + child: Text( + groupElement.title!, + style: + themeData.textTheme.titleSmall!.apply(color: Colors.grey), + ), ), ), for (final String panelId in groupElement.panelIds) diff --git a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_image_card.dart b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_image_card.dart index b44ee88d481..263f8135df3 100644 --- a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_image_card.dart +++ b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_image_card.dart @@ -1,6 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/helpers/launch_url_helper.dart'; /// Card that displays a Knowledge Panel _Image_ element. class KnowledgePanelImageCard extends StatelessWidget { @@ -12,11 +13,21 @@ class KnowledgePanelImageCard extends StatelessWidget { // TODO(g123k): It would be nice to provide a Placeholder @override - Widget build(BuildContext context) => Image.network( - imageElement.url, - width: imageElement.width?.toDouble(), - height: imageElement.height?.toDouble(), - ); + Widget build(BuildContext context) { + final Widget image = Image.network( + imageElement.url, + width: imageElement.width?.toDouble(), + height: imageElement.height?.toDouble(), + ); + final String? linkUrl = imageElement.linkUrl; + if (linkUrl == null) { + return image; + } + return InkWell( + onTap: () => LaunchUrlHelper.launchURL(linkUrl), + child: image, + ); + } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { diff --git a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_page.dart b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_page.dart index 3d0ead01919..93eb004619a 100644 --- a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_page.dart +++ b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:matomo_tracker/matomo_tracker.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; @@ -9,8 +10,8 @@ import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/knowledge_panel/knowledge_panels/knowledge_panel_expanded_card.dart'; import 'package:smooth_app/knowledge_panel/knowledge_panels_builder.dart'; -import 'package:smooth_app/pages/carousel_manager.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; +import 'package:smooth_app/pages/scan/carousel/scan_carousel_manager.dart'; import 'package:smooth_app/widgets/smooth_app_bar.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; @@ -50,13 +51,19 @@ class _KnowledgePanelPageState extends State @override Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final String title = _getTitle(); + context.watch(); refreshUpToDate(); return SmoothScaffold( appBar: SmoothAppBar( - title: Text( - _getTitle(), - maxLines: 2, + title: Semantics( + label: _getTitleForAccessibility(appLocalizations, title), + child: Text( + title, + maxLines: 2, + ), ), ), body: RefreshIndicator( @@ -96,7 +103,7 @@ class _KnowledgePanelPageState extends State Future _refreshProduct(BuildContext context) async { try { final String? barcode = - ExternalCarouselManager.read(context).currentBarcode; + ExternalScanCarouselManager.read(context).currentBarcode; if (barcode?.isEmpty == true) { return; } @@ -126,6 +133,24 @@ class _KnowledgePanelPageState extends State return ''; } + String _getTitleForAccessibility( + AppLocalizations appLocalizations, + String title, + ) { + final String productName = upToDateProduct.productName ?? + upToDateProduct.abbreviatedName ?? + upToDateProduct.genericName ?? + ''; + if (title.isEmpty) { + return appLocalizations.knowledge_panel_page_title_no_title(productName); + } else { + return appLocalizations.knowledge_panel_page_title( + title, + productName, + ); + } + } + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); diff --git a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_text_card.dart b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_text_card.dart index 1d390fdd824..fb09b1747d5 100644 --- a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_text_card.dart +++ b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_text_card.dart @@ -18,9 +18,11 @@ class KnowledgePanelTextCard extends StatelessWidget { @override Widget build(BuildContext context) { - final Widget text = SmoothHtmlWidget( - textElement.html, - textStyle: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, + final Widget text = MergeSemantics( + child: SmoothHtmlWidget( + textElement.html, + textStyle: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, + ), ); if (!_hasSource) { diff --git a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_title_card.dart b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_title_card.dart index ae51b6fc00a..743683927e9 100644 --- a/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_title_card.dart +++ b/packages/smooth_app/lib/knowledge_panel/knowledge_panels/knowledge_panel_title_card.dart @@ -79,6 +79,7 @@ class KnowledgePanelTitleCard extends StatelessWidget { child: Semantics( value: _generateSemanticsValue(context), button: isClickable, + container: true, excludeSemantics: true, child: Row( children: [ diff --git a/packages/smooth_app/lib/l10n/app_aa.arb b/packages/smooth_app/lib/l10n/app_aa.arb index 9fe05c9c847..4c629c401ac 100644 --- a/packages/smooth_app/lib/l10n/app_aa.arb +++ b/packages/smooth_app/lib/l10n/app_aa.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_af.arb b/packages/smooth_app/lib/l10n/app_af.arb index f256ceffbe9..21d0f14f6cb 100644 --- a/packages/smooth_app/lib/l10n/app_af.arb +++ b/packages/smooth_app/lib/l10n/app_af.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Neem 'n foto", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ak.arb b/packages/smooth_app/lib/l10n/app_ak.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_ak.arb +++ b/packages/smooth_app/lib/l10n/app_ak.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_am.arb b/packages/smooth_app/lib/l10n/app_am.arb index d4a5c9e0f71..416ed2c77ed 100644 --- a/packages/smooth_app/lib/l10n/app_am.arb +++ b/packages/smooth_app/lib/l10n/app_am.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ar.arb b/packages/smooth_app/lib/l10n/app_ar.arb index 97af742a1f5..1f9dcc97150 100644 --- a/packages/smooth_app/lib/l10n/app_ar.arb +++ b/packages/smooth_app/lib/l10n/app_ar.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "صورة المكونات", "nutritional_facts_photo_title": "صورة الحقائق الغذائية", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "التقاط صورة", "take_more_photo_title": "التقط المزيد من الصور", "front_photo_uploaded": "تم تحميل الصورة الأمامية", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "لم يتم العثور على أي منتج", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "لم يتم العثور على نتائج:", - "searchPanelHeader": "ابحث عن أول منتج لك، أو اعمل مسح ضوئي للكود. ", - "@Product query status": {}, "refreshing_product": "تحديث المنتج", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "يتعذر جلب معلمومات حول هذا المنتج بسبب خطأ في الشبكة.", "cached_results_from": "إظهار النتائج من:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "الفئات", - "edit_ingredients_extrait_ingredients_btn_text": "استخراج المكونات", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "تحديث الصورة", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "تحديث الصورة", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "تم", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "معطيات", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "النتيجة البيئية", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "مشاركة", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_as.arb b/packages/smooth_app/lib/l10n/app_as.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_as.arb +++ b/packages/smooth_app/lib/l10n/app_as.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_az.arb b/packages/smooth_app/lib/l10n/app_az.arb index 5012cb64689..7b7b86c1735 100644 --- a/packages/smooth_app/lib/l10n/app_az.arb +++ b/packages/smooth_app/lib/l10n/app_az.arb @@ -31,8 +31,8 @@ "go_back_to_top": "Yuxarıya qayıt", "save": "Save", "save_confirmation": "Yadda saxlamaq istədiyinizə əminsiniz?", - "skip": "Skip", - "cancel": "Cancel", + "skip": "Ötür", + "cancel": "İmtina", "@cancel": {}, "ignore": "Rədd et", "@ignore": { @@ -151,7 +151,7 @@ "incorrect_credentials": "Incorrect username or password.", "password_lost_incorrect_credentials": "This email or username doesn't exist. Please check your credentials.", "password_lost_server_unavailable": "We are currently experiencing slowdowns on our servers and we apologise for it. Please try again later.", - "login": "Login", + "login": "Daxil ol", "@login": { "description": "Text field hint: unified name for either username or e-mail address" }, @@ -505,7 +505,7 @@ "@unknownBrand": {}, "unknownProductName": "Unknown product name", "@unknownProductName": {}, - "label_refresh": "Refresh", + "label_refresh": "Yenilə", "@label_refresh": { "description": "Refresh the cached product" }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1209,7 +1288,7 @@ "permission_photo_denied_dialog_settings_title": "Permission denied", "permission_photo_denied_dialog_settings_message": "As you've previously denied the camera permission, you must allow it manually from the Settings.", "permission_photo_denied_dialog_settings_button_open": "Open settings", - "permission_photo_denied_dialog_settings_button_cancel": "Cancel", + "permission_photo_denied_dialog_settings_button_cancel": "İmtina", "permission_photo_none_found": "No camera detected", "@permission_photo_none_found": { "description": "Message for the user when no camera was detected, replacing the barcode scanner" @@ -1339,15 +1418,15 @@ "@edit_product_form_item_emb_codes_examples": { "description": "Product edition - EMB Codes - explanations" }, - "edit_product_form_item_categories_title": "Categories", + "edit_product_form_item_categories_title": "Kateqoriyalar", "@edit_product_form_item_categories_title": { "description": "Product edition - Categories - Title" }, - "edit_product_form_item_categories_hint": "category", + "edit_product_form_item_categories_hint": "kateqoriya", "@edit_product_form_item_categories_hint": { "description": "Product edition - Categories - input textfield hint" }, - "edit_product_form_item_categories_type": "category", + "edit_product_form_item_categories_type": "kateqoriya", "@edit_product_form_item_categories_type": { "description": "Product edition - Categories - input textfield type" }, @@ -1385,7 +1464,7 @@ "@edit_product_form_item_nutrition_facts_subtitle": { "description": "Product edition - Nutrition facts - SubTitle" }, - "edit_product_form_save": "Edit", + "edit_product_form_save": "Düzəliş etmək", "@edit_product_form_save": { "description": "Product edition - Nutrition facts - Save button" }, @@ -1399,19 +1478,35 @@ }, "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", - "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "category_picker_page_appbar_text": "Kateqoriyalar", + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1598,7 +1693,7 @@ "@dev_preferences_button_positive": { "description": "User dev preferences - Positive button label" }, - "dev_preferences_button_negative": "Cancel", + "dev_preferences_button_negative": "İmtina", "@dev_preferences_button_negative": { "description": "User dev preferences - Negative button label" }, @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1736,7 +1977,7 @@ "@summary_card_button_add_basic_details": { "description": "Summary card - Button to add details about the product" }, - "edit_photo_button_label": "Edit", + "edit_photo_button_label": "Düzəliş etmək", "@edit_photo_button_label": { "description": "Edit photo button label" }, @@ -1768,7 +2009,7 @@ "@edit_photo_language_none": { "description": "Warning message: for this product and this field, there are no images at all, in any language" }, - "category_picker_screen_title": "Categories", + "category_picker_screen_title": "Kateqoriyalar", "@category_picker_screen_title": { "description": "Categories picker screen title" }, @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Paylaş", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_be.arb b/packages/smooth_app/lib/l10n/app_be.arb index 9ae4493d784..f573d468abb 100644 --- a/packages/smooth_app/lib/l10n/app_be.arb +++ b/packages/smooth_app/lib/l10n/app_be.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Фота прадукту спераду", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Пацвердзіце запампоўку фота прадукту спераду", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Фота інгрэдыентаў", "nutritional_facts_photo_title": "Фота звестак аб пажыўнасці", "recycling_photo_title": "Фота звестак аб перапрацоўцы", + "take_photo_title": "Сфатаграфаваць", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Фота спераду запампавана", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Прадукт не знойдзены", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "не знойдзена:", - "searchPanelHeader": "Шукайце або скануйце свой першы прадукт", - "@Product query status": {}, "refreshing_product": "Абнаўленне прадукту", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Немагчыма атрымаць інфармацыю аб гэтым прадукце з-за памылкі сеткі.", "cached_results_from": "Паказаць вынікі за:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "У 2012 годзе\nмы вынайшлі праграму\nдля сумеснага сканавання", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Паколькі нам «стукнула»10 гадоў,\nмы вынаходзім яе\nз нуля!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Працягнуць", "onboarding_welcome_loading_dialog_title": "Загружаецца ваш першы прыклад прадукту", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Вібрацыя і тактыльны водгук", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Запоўніце асноўныя звесткі", "not_implemented_snackbar_text": "Яшчэ не рэалізавана", "category_picker_page_appbar_text": "Катэгорыі", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Абнавіць фота", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Распазнаць упакоўку", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Абнавіць фота", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Даныя", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Выключыць Eco-ацэнку", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Усе недаапрацаваныя прадукты", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_bg.arb b/packages/smooth_app/lib/l10n/app_bg.arb index 04486e8eee8..4f79aa0a9fa 100644 --- a/packages/smooth_app/lib/l10n/app_bg.arb +++ b/packages/smooth_app/lib/l10n/app_bg.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Снимка на продукта отпред", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Потвърди качването на снимката на предната страна на продукта", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Снимка на съставките", "nutritional_facts_photo_title": "Снимка на хранителните стойности", "recycling_photo_title": "Снимка на указанията за рециклиране", + "take_photo_title": "Направи снимка", "take_more_photo_title": "Направи още снимки", "front_photo_uploaded": "Снимката на предната страна е качена", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Няма намерен продукт", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "не е намерено:", - "searchPanelHeader": "Потърси или сканирай първия си продукт", - "@Product query status": {}, "refreshing_product": "Актуализация на продукта", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Харесваш ли приложението?", "tagline_app_review_button_positive": "Обичам го! 😍", "tagline_app_review_button_negative": "Не особено…", "tagline_app_review_button_later": "Попитай по-късно", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Не харесваш приложението?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Невъзможно е да се извлече информация за този продукт поради грешка в мрежата.", "cached_results_from": "Кеширани резултати от:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Моля, избери страна:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Измислихме приложението\nза съвместно сканиране \nпрез 2012 г.", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "10 години по-късно\nго преоткриваме\nот самото начало!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Продължи", "onboarding_welcome_loading_dialog_title": "Зареждане на първия ти примерен продукт", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Вибрация и усещане", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Попълни основните подробности", "not_implemented_snackbar_text": "Все още не е внедрено", "category_picker_page_appbar_text": "Категории", - "edit_ingredients_extrait_ingredients_btn_text": "Извлечи съставките", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Обнови снимката", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Извличане на опаковката", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Обнови снимката", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Продъктът не е открит", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Готово", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Данни", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Изключи Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Всички продукти, които трябва да бъдат завършени", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Стартиране на опресняването на всички продукти, съхранявани локално", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Избери език:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Инсталирай Open Beauty Facts, за да създадеш козметична база данни", "faq_title_install_pet": "Инсталирай Open Pet Food Facts, за да създадеш база данни с храни за домашни любимци", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Сподели", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "bg", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_bm.arb b/packages/smooth_app/lib/l10n/app_bm.arb index 54e0b04ca38..034656adb48 100644 --- a/packages/smooth_app/lib/l10n/app_bm.arb +++ b/packages/smooth_app/lib/l10n/app_bm.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Suguyaw", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_bn.arb b/packages/smooth_app/lib/l10n/app_bn.arb index bda95f8abf3..df9e896913f 100644 --- a/packages/smooth_app/lib/l10n/app_bn.arb +++ b/packages/smooth_app/lib/l10n/app_bn.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "একটি ছবি নিন", "take_more_photo_title": "আরও কিছু ছবি তুলুন", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "শ্রেণীসমূহ", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "তথ্য", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "শেয়ার", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_bo.arb b/packages/smooth_app/lib/l10n/app_bo.arb index 0582593abcc..d880cf68188 100644 --- a/packages/smooth_app/lib/l10n/app_bo.arb +++ b/packages/smooth_app/lib/l10n/app_bo.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_br.arb b/packages/smooth_app/lib/l10n/app_br.arb index 1bbaff47003..a47c5ccddb6 100644 --- a/packages/smooth_app/lib/l10n/app_br.arb +++ b/packages/smooth_app/lib/l10n/app_br.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Tapout ur skeudenn", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Rummadoù", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Graet", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Roadennoù", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Rannañ", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_bs.arb b/packages/smooth_app/lib/l10n/app_bs.arb index b4ce0945a6a..9f80e3cf392 100644 --- a/packages/smooth_app/lib/l10n/app_bs.arb +++ b/packages/smooth_app/lib/l10n/app_bs.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategorije", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ca.arb b/packages/smooth_app/lib/l10n/app_ca.arb index fa7744bd05a..cd0811b7bed 100644 --- a/packages/smooth_app/lib/l10n/app_ca.arb +++ b/packages/smooth_app/lib/l10n/app_ca.arb @@ -145,9 +145,9 @@ "@ecoCardUtility": { "description": "Description of what a user can use the Eco data in a product for." }, - "server_error_open_new_issue": "No server response! You may open an issue with the following link.", + "server_error_open_new_issue": "No hi ha resposta del servidor! Podeu obrir una incidència amb el següent enllaç.", "@user_management": {}, - "sign_in_text": "Sign in to your Open Food Facts account to save your contributions", + "sign_in_text": "Inicieu la sessió al vostre compte d'Open Food Facts per desar les vostres contribucions", "incorrect_credentials": "Usuari o contrasenya incorrectes.", "password_lost_incorrect_credentials": "This email or username doesn't exist. Please check your credentials.", "password_lost_server_unavailable": "We are currently experiencing slowdowns on our servers and we apologise for it. Please try again later.", @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Foto frontal del producte", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirma la càrrega de la foto frontal del producte", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Foto dels ingredients", "nutritional_facts_photo_title": "Foto dels valors nutricionals", "recycling_photo_title": "Foto del reciclatge", + "take_photo_title": "Fer una foto", "take_more_photo_title": "Feu més fotos", "front_photo_uploaded": "S'ha penjat la foto frontal", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Cap producte trobat", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "no trobat:", - "searchPanelHeader": "Cerqueu o escanegeu el primer producte", - "@Product query status": {}, "refreshing_product": "Producte refrescant", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Us agrada l'aplicació?", "tagline_app_review_button_positive": "M'encanta! 😍", "tagline_app_review_button_negative": "No gaire…", "tagline_app_review_button_later": "Pregunta-m'ho després", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "No us agrada, la nostra aplicació?", "app_review_negative_modal_text": "Podríeu emprar uns minuts per a dir-nos el per què?", "app_review_negative_modal_positive_button": "I tant!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "No es pot obtenir informació sobre aquest producte a causa d'un error de xarxa.", "cached_results_from": "Mostra resultats de:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -818,7 +895,7 @@ "@native_app_settings": { "description": "Native App Settings in app settings" }, - "native_app_description": "Open systems settings for Open Food Facts", + "native_app_description": "Obre la configuració del sistema per a Open Food Facts", "@native_app_description": { "description": "Native App description in app settings" }, @@ -837,7 +914,7 @@ "@Lists": {}, "no_prodcut_in_list": "No hi ha cap producte en aquesta llista", "no_product_in_section": "No hi ha cap producte en aquesta secció", - "recently_seen_products": "All viewed products", + "recently_seen_products": "Tots els productes vistos", "clear": "Esborra", "@clear": { "description": "Clears a product list (short label)" @@ -911,12 +988,12 @@ }, "retry_button_label": "Torna a provar", "connect_with_us": "Connecta amb nosaltres", - "instagram": "Follow us on Instagram", + "instagram": "Segueix-nos a Instagram", "instagram_link": "https://instagram.com/open.food.facts", - "twitter": "Follow us on X (formerly Twitter)", + "twitter": "Segueix-nos a X (abans Twitter)", "twitter_link": "https://www.twitter.com/openfoodfacts", "blog": "Bloc", - "faq": "FAQ", + "faq": "PMF", "discover": "Descobrir", "how_to_contribute": "Com contribuir", "hint_knowledge_panel_message": "Podeu fer clic a qualsevol part de la targeta per obtenir més detalls del que veieu. Proveu-ho ara!", @@ -927,7 +1004,7 @@ "@consent_analytics_title": { "description": "Title for the consent analytics UI Page" }, - "consent_analytics_body1": "Help the Open Food Facts volunteers to improve the app. You decide whether to submit anonymous analytics.", + "consent_analytics_body1": "Ajudeu als voluntaris d'Open Food Facts a millorar l'aplicació. Decidiu si voleu enviar analítiques anònimes.", "@conset_analytics_body1": { "description": "first paragraph for the consent analytics UI Page" }, @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Vam inventar\nl'aplicació col·laborativa\nd'escaneig l'any 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Ja que en fem 10,\nl'estem reinventant\ndes de zero!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continuar", "onboarding_welcome_loading_dialog_title": "S'està carregant el vostre primer exemple de producte", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibració i hàptica", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Detalls bàsics complets", "not_implemented_snackbar_text": "Encara no s'ha implementat", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extreu els ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Actualitza la foto", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extreure l'embalatge", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Actualitza la foto", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Afegir un preu", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "No s'ha trobat el producte", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Preu", + "prices_amount_price_discounted": "Preu rebaixat", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Data", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Prova", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Fet", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Dades", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclou l'Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Tots els productes per completar", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Començant l'actualització de tots els productes emmagatzemats localment", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", - "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_new_formula": "Nutri-Score {letter} (nou càlcul)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (nou càlcul)", + "nutriscore_unknown": "Nutri-Score desconegut", + "nutriscore_unknown_new_formula": "Nutri-score desconegut (nou càlcul)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Aquest enllaç no es pot obrir al vostre dispositiu. Si us plau, comproveu que teniu un navegador instal·lat.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Detalls per a {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Detalls per a {pageName} amb {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Comparteix", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ce.arb b/packages/smooth_app/lib/l10n/app_ce.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_ce.arb +++ b/packages/smooth_app/lib/l10n/app_ce.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_co.arb b/packages/smooth_app/lib/l10n/app_co.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_co.arb +++ b/packages/smooth_app/lib/l10n/app_co.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_cs.arb b/packages/smooth_app/lib/l10n/app_cs.arb index 251c07638b0..aac58d30cc8 100644 --- a/packages/smooth_app/lib/l10n/app_cs.arb +++ b/packages/smooth_app/lib/l10n/app_cs.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Ilustrace s neznámým Nutri-Score a Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Přední fotka produktu", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Potvrdit nahrání přední fotky produktu", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Fotografie ingrediencí", "nutritional_facts_photo_title": "Fotografie nutričních údajů", "recycling_photo_title": "Fotografie recyklace", + "take_photo_title": "Vyfotit", "take_more_photo_title": "Vyfotit další obrázky", "front_photo_uploaded": "Přední fotka nahrána", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Nebyl nalezen žádný produkt", "@no_product_found": {}, + "no_location_found": "Nenalezena žádná poloha", "not_found": "nenalezeno:", - "searchPanelHeader": "Vyhledejte nebo naskenujte svůj první produkt", - "@Product query status": {}, "refreshing_product": "Obnovování produktu", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Vyhledat produkt", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Líbí se vám aplikace?", "tagline_app_review_button_positive": "Miluji to! 😍", "tagline_app_review_button_negative": "Ani ne...", "tagline_app_review_button_later": "Zeptejte se mě později", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Nelíbí se vám naše aplikace?", "app_review_negative_modal_text": "Mohl byste nám dát pár sekund a říct nám proč?", "app_review_negative_modal_positive_button": "Ano, samozřejmě!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "Vyskytla se chyba!", + "product_internet_error_modal_message": "Nemůžeme načíst informace o tomto produktu kvůli chybě sítě. Zkontrolujte prosím připojení k internetu a zkuste to znovu.\n\nVnitřní chyba:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Nelze načíst informace o tomto produktu kvůli chybě sítě.", "cached_results_from": "Zobrazit výsledky z:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Prosím zvolte měnu", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Právě jste změnili zemi.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Chcete změnit měnu z {previousCurrency} na {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Vyberte prosím zemi:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "V roce 2012 jsme vynalezli\nkooperativní\naplikaci pro skenování", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Jelikož je nám 10 let, vymýšlíme ji od základu znovu!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Pokračovat", "onboarding_welcome_loading_dialog_title": "Načítám váš první příklad produktu", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibrace a haptika", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Vyplňte základní údaje", "not_implemented_snackbar_text": "Zatím neimplementováno", "category_picker_page_appbar_text": "Kategorie", - "edit_ingredients_extrait_ingredients_btn_text": "Aut. rozpoznat složení", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Obnovit fotku", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extrakt balení", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Obnovit fotku", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Zástupce pro aplikaci Ceny na produktové stránce", "prices_app_button": "Přejít do aplikace Ceny", + "prices_generic_title": "Ceny", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Přidat cenu", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Produkt nebyl nalezen", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Hledat produkt", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "Zobrazit ceny", + "prices_list_length_one_page": "{count,plural, =0{Zatím žádná cena} =1{Pouze 1 cena} few{Všechny {count} ceny} other{Všech {count} cen}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Posledních {pageSize} cen (celkem: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Množství", + "prices_amount_is_discounted": "Je zlevněné?", + "prices_amount_price_normal": "Cena", + "prices_amount_price_discounted": "Snížená cena", + "prices_amount_price_not_discounted": "Původní cena", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Nesprávná hodnota", + "prices_amount_price_mandatory": "Povinná hodnota", + "prices_currency_subtitle": "Měna", + "prices_date_subtitle": "Datum", + "prices_location_subtitle": "Obchod", + "prices_location_find": "Najít obchod", + "prices_location_mandatory": "Musíte vybrat obchod!", + "prices_proof_subtitle": "Důkaz", + "prices_proof_find": "Vybrat důkaz", + "prices_proof_receipt": "Účtenka", + "prices_proof_price_tag": "Cenovka", + "prices_proof_mandatory": "Musíte vybrat důkaz!", + "prices_add_validation_error": "Chyba ověření", + "prices_privacy_warning_title": "Upozornění na soukromí", + "prices_privacy_warning_message": "Ceny budou veřejné, spolu s obchodem, na který odkazují.\nTo by mohlo umožnit lidem, kteří znají váš pseudonym Open Food Facts:\n* odvodit, ve které oblasti žijete\n* vědět, co kupujete\nPokud s tím jste znepokojeni, změňte si prosím svůj pseudonym nebo si vytvořte nový účet Open Food Facts a přihlaste se pomocí něj do aplikace.", + "prices_unknown_product": "Neznámý produkt", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Hotovo", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Údaje", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Vyloučit Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Znovu použijte a upravte toto vyhledávání", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Moje ceny", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Moje důkazy", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Přidány nejnovější ceny", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Nejlepší přispěvatelé cen", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Obchody s nejvíce cenami", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Produkty s nejvíce cenami", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Všechny produkty k dokončení", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "Cena bude zaslána na server co nejdříve.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Spouštění aktualizace všech lokálně uložených produktů", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Vyberte svou měnu:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Vyberte svůj jazyk:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (Nový výpočet)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (Nový výpočet)", "nutriscore_unknown": "Neznámé Nutri-Score", + "nutriscore_unknown_new_formula": "Neznámé Nutri-Score (Nový výpočet)", "nutriscore_not_applicable": "Nutri-Score nelze použít", + "nutriscore_not_applicable_new_formula": "Nutri-Score nelze použít (Nový výpočet)", "ecoscore_generic": "Eco-score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Nainstalujte Open Beauty Facts a vytvořte kosmetickou databázi", "faq_title_install_pet": "Nainstalujte si Open Pet Food Facts a vytvořte databázi krmiv pro domácí zvířata", "faq_title_install_product": "Nainstalujte Open Products Facts pro vytvoření databáze produktů pro prodloužení životnosti objektů", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Importujte své produkty do Open Food Facts", "contact_title_pro_email": "Kontakt výrobce", "contact_title_press_page": "Pro média", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Tento odkaz nelze na vašem zařízení otevřít. Zkontrolujte, zda máte nainstalovaný prohlížeč.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Podrobnosti o {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Podrobnosti o {pageName} s {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Průvodce", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Sdílet", + "guide_nutriscore_v2_enabled": "pravda", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "Nutri-Score se vyvíjí: vysvětlení!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Co je Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "Nutri-Score je logo, jehož cílem je informovat vás o **nutriční kvalitě potravin**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "Barevný kód se liší od tmavě zelené (**A**) pro **nejzdravější** produkty až po tmavě červenou (**E**) pro **méně zdravé**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "Logo Nutri-Score A", + "guide_nutriscore_v2_why_v2_title": "Proč se Nutri-Score vyvíjí?", + "guide_nutriscore_v2_why_v2_intro": "Vzorec pro Nutri-Score **se vyvíjí** k poskytování lepších doporučení:", + "guide_nutriscore_v2_why_v2_arg1_title": "Lepší hodnocení všech nápojů", + "guide_nutriscore_v2_why_v2_arg1_text": "Srovnávací poznámky **mléka**, **mléčných nápojů** s přidaným cukrem a **zeleninových** nápojů byly v novém algoritmu lépe rozlišeny.", + "guide_nutriscore_v2_why_v2_arg2_title": "Lepší hodnocení nápojů", + "guide_nutriscore_v2_why_v2_arg2_text": "**Obsah cukru** je lépe zohledněn a upřednostňuje **nízce slazené** nápoje.\\n**Sladidla budou rovněž penalizována**: dietní limonády budou sníženy z hodnocení B na hodnotu mezi C a E. Doporučeným nápojem zůstává voda.", + "guide_nutriscore_v2_why_v2_arg3_title": "Sůl a cukr penalizovány", + "guide_nutriscore_v2_why_v2_arg3_text": "**Příliš sladké** nebo **příliš slané** produkty budou mít své **hodnocení dále sníženo**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchie v olejích a rybách", + "guide_nutriscore_v2_why_v2_arg4_text": "Hodnocení určitých **tučných ryb** a **olejů bohatých na dobré tuky** se zlepší.", + "guide_nutriscore_v2_why_v2_arg5_title": "Omezené červené maso", + "guide_nutriscore_v2_why_v2_arg5_text": "Konzumace **červeného masa by měla být omezena**. To je důvod, proč **drůbež bude mít poměrně lepší hodnocení**.", + "guide_nutriscore_v2_new_logo_title": "Jak odlišit staré Nutri-Score a nový výpočet?", + "guide_nutriscore_v2_new_logo_text": "Od této chvíle může logo zobrazovat zmínku „**Nový výpočet**“, aby bylo jasné, že se skutečně jedná o nový výpočet.", + "guide_nutriscore_v2_new_logo_image_caption": "Logo nového Nutri-Score", + "guide_nutriscore_v2_where_title": "Kde najít nový výpočet Nutri-Score?", + "guide_nutriscore_v2_where_paragraph1": "Nutri-Score se používá v několika zemích: Německo, Belgie, Španělsko, Francie, Lucembursko, Nizozemsko a Švýcarsko.", + "guide_nutriscore_v2_where_paragraph2": "Výrobci mají nejpozději do **2026** čas **nahradit** starý výpočet novým.", + "guide_nutriscore_v2_where_paragraph3": "Bez čekání **už najdete v aplikaci OpenFoodFacts** nový výpočet, včetně případů, kdy výrobci neaktualizovali skóre.", + "guide_nutriscore_v2_unchanged_title": "Co se nemění", + "guide_nutriscore_v2_unchanged_paragraph1": "Nutri-Score je skóre navržené k **měření nutriční kvality**. Je **doplňkem skupiny NOVA** pro **ultra zpracované potraviny** (taktéž přítomných v aplikaci).", + "guide_nutriscore_v2_unchanged_paragraph2": "Pro výrobce zůstává zobrazení Nutri-Score **nepovinné**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_cv.arb b/packages/smooth_app/lib/l10n/app_cv.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_cv.arb +++ b/packages/smooth_app/lib/l10n/app_cv.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_cy.arb b/packages/smooth_app/lib/l10n/app_cy.arb index dc7cf09ccd7..1c3b2975c95 100644 --- a/packages/smooth_app/lib/l10n/app_cy.arb +++ b/packages/smooth_app/lib/l10n/app_cy.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Tynnu llun", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categorïau", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_da.arb b/packages/smooth_app/lib/l10n/app_da.arb index c56ef451518..90bc68b80ec 100644 --- a/packages/smooth_app/lib/l10n/app_da.arb +++ b/packages/smooth_app/lib/l10n/app_da.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Produktforsidefoto", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Bekræft upload af produktforsidefoto", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingrediensfoto", "nutritional_facts_photo_title": "Næringsfaktafoto", "recycling_photo_title": "Genbrugsfoto", + "take_photo_title": "Tag et billede", "take_more_photo_title": "Tag flere billeder", "front_photo_uploaded": "Forsidefoto uploadet", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Intet produkt fundet", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "ikke fundet:", - "searchPanelHeader": "Søg eller skan dit første produkt", - "@Product query status": {}, "refreshing_product": "Opfrisker produkt", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Kan du lide appen?", "tagline_app_review_button_positive": "Jeg elsker det! 😍", "tagline_app_review_button_negative": "Ikke rigtigt…", "tagline_app_review_button_later": "Spørg mig senere", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Kan du ikke lide vores app?", "app_review_negative_modal_text": "Vil du bruge et par sekunder på at fortælle os hvorfor?", "app_review_negative_modal_positive_button": "Ja, selvfølgelig!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Ikke muligt at hente disse produktoplysninger grundet netværksfejl.", "cached_results_from": "Vis resultater fra:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Vi opfandt\nden kollaborative\nskannings-app i 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Da vi fylder 10 år,\nvi genopfinder den\nfra bunden!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Velkommen!", + "onboarding_home_welcome_text2": "Appen, der hjælper dig med at vælge mad, der er godt for **dig** og **planeten**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Fortsæt", "onboarding_welcome_loading_dialog_title": "Indlæser første eksempelprodukt", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration og haptik", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Udfyld basisoplysninger", "not_implemented_snackbar_text": "Endnu ikke implementeret", "category_picker_page_appbar_text": "Kategorier", - "edit_ingredients_extrait_ingredients_btn_text": "Udtræk ingredienser", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Opfrisk foto", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Udtræk emballage", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Opfrisk foto", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Produktet blev ikke fundet", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Stregkodelæser", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Prisskilt", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Færdiggjort", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Undtag Øko-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Genbrug og redigér denne søgning", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Alle produkter til færdiggørelse", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starter opdateringen af alle de lokalt lagrede produkter", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Øko-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "Ny beregning af Nutri-Score: hvad er nyt?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Del", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_de.arb b/packages/smooth_app/lib/l10n/app_de.arb index bb48c1111e3..9af6a20011c 100644 --- a/packages/smooth_app/lib/l10n/app_de.arb +++ b/packages/smooth_app/lib/l10n/app_de.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Eine Abbildung mit unbekanntem Nutri-Score und Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Foto von der Vorderseite des Produkts", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Hochladen des Fotos der Vorderseite des Produkts bestätigen", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Foto der Zutatenliste", "nutritional_facts_photo_title": "Foto der Nährwertangaben", "recycling_photo_title": "Foto der Recycling-Informationen", + "take_photo_title": "Foto aufnehmen", "take_more_photo_title": "Weitere Fotos aufnehmen", "front_photo_uploaded": "Foto der Vorderseite der Verpackung hochgeladen", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Keine Produkte gefunden", "@no_product_found": {}, + "no_location_found": "Kein Standort gefunden", "not_found": "Nicht gefunden:", - "searchPanelHeader": "Suchen oder scannen Sie Ihr erstes Produkt", - "@Product query status": {}, "refreshing_product": "Produkt wird aktualisiert", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Foto aufgenommen am {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Foto aufgenommen am {date}. Dieses Foto kann veraltet sein", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Willkommen bei Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scannen** eines Barcodes oder\n**Suchen** nach einem Produkt", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Produkt suchen", + "homepage_main_card_search_field_tooltip": "Suche starten", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Neueste Nachrichten: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Gefällt Ihnen die App?", "tagline_app_review_button_positive": "Ich liebe sie! 😍", "tagline_app_review_button_negative": "Nicht wirklich …", "tagline_app_review_button_later": "Später erneut fragen", + "tagline_feed_news_button": "Mehr erfahren", "app_review_negative_modal_title": "Gefällt Ihnen diese App?", "app_review_negative_modal_text": "Könnten Sie sich ein paar Sekunden Zeit nehmen und uns sagen, warum?", "app_review_negative_modal_positive_button": "Ja, natürlich!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "Ein Fehler ist aufgetreten!", + "product_internet_error_modal_message": "Aufgrund eines Netzwerkfehlers konnten keine Informationen zu diesem Produkt abgerufen werden. Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.\n\nInterner Fehler:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Informationen über dieses Produkt können aufgrund eines Netzwerkfehlers nicht abgerufen werden.", "cached_results_from": "Ergebnisse anzeigen von:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Bitte wählen Sie eine Währung", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Sie haben gerade das Land gewechselt.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Möchten Sie die Währung von {previousCurrency} in {possibleCurrency} ändern?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Bitte wählen Sie ein Land:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Wir haben 2012 die\nApp für kollaboratives\nScannen erfunden", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Anlässlich unseres\n10-jährigen Bestehens\nerfinden wir es von Grund auf neu!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Willkommen!", + "onboarding_home_welcome_text2": "Die App, die Ihnen hilft, Lebensmittel auszuwählen, die gut für **Sie** und den **Planeten** sind!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Weiter", "onboarding_welcome_loading_dialog_title": "Ihr erstes Beispielprodukt wird geladen", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Lesen Sie einen Barcode mit Ihrer Kamera ein", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration und haptisches Feedback", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Allgemeine Angaben vervollständigen", "not_implemented_snackbar_text": "Noch nicht implementiert", "category_picker_page_appbar_text": "Kategorien", - "edit_ingredients_extrait_ingredients_btn_text": "Zutaten extrahieren", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Zutaten aus dem Foto extrahieren", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Zutaten werden aus\ndem Foto extrahiert", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Foto wird geladen …", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Foto aktualisieren", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Verpackung extrahieren", + "edit_packaging_extract_btn_text": "Verpackung aus dem\nFoto extrahieren", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Verpackung wird aus dem Foto extrahiert", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Foto wird geladen …", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Foto aktualisieren", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Verknüpfung zur Preise-App auf der Produktseite", "prices_app_button": "Zur Preise-App wechseln", + "prices_generic_title": "Preise", + "prices_add_n_prices": "{count,plural, one {}=1{Preis hinzufügen} other{{count} Preise hinzufügen}}", + "prices_send_n_prices": "{count,plural, one {}=1{Preis senden} other{{count} Preise senden}}", + "prices_add_an_item": "Ein Element hinzufügen", + "prices_add_a_price": "Preis hinzufügen", + "prices_add_a_receipt": "Beleg hinzufügen", + "prices_add_price_tags": "Preisschilder hinzufügen", + "prices_barcode_search_not_found": "Produkt nicht gefunden", + "prices_barcode_search_none_yet": "Noch kein Produkt", + "prices_barcode_search_question": "Möchten Sie nach diesem Produkt suchen?", + "prices_barcode_search_title": "Produkte suchen", + "prices_barcode_search_running": "Nach {barcode} suchen", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcodelesegerät", + "prices_view_prices": "Preise anzeigen", + "prices_list_length_one_page": "{count,plural, =0{Noch kein Preis} =1{Nur ein Preis} other{Alle {count} Preise}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Letzte {pageSize} Preise (insgesamt: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, one {}=0{Noch kein Nachweis} =1{Nur ein Nachweis} other{Alle {count} Nachweise}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Letzte {pageSize} Nachweise (insgesamt: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Die besten {pageSize} Mitwirkende (insgesamt: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Betrag", + "prices_amount_is_discounted": "Preisreduziert?", + "prices_amount_price_normal": "Preis", + "prices_amount_price_discounted": "Reduzierter Preis", + "prices_amount_price_not_discounted": "Ursprünglicher Preis", + "prices_amount_no_product": "Ein Produkt fehlt!", + "prices_amount_price_incorrect": "Ungültiger Wert", + "prices_amount_price_mandatory": "Notwendiger Wert", + "prices_currency_subtitle": "Währung", + "prices_date_subtitle": "Datum", + "prices_location_subtitle": "Laden", + "prices_location_find": "Einen Laden finden", + "prices_location_mandatory": "Sie müssen einen Laden auswählen!", + "prices_proof_subtitle": "Nachweis", + "prices_proof_find": "Wählen Sie einen Nachweis", + "prices_proof_receipt": "Kassenbon", + "prices_proof_price_tag": "Preisschild", + "prices_proof_mandatory": "Sie müssen einen Nachweis auswählen!", + "prices_add_validation_error": "Überprüfungsfehler", + "prices_privacy_warning_title": "Datenschutzwarnung", + "prices_privacy_warning_message": "Die Preise werden zusammen mit dem Laden, auf den sie sich beziehen, veröffentlicht.\nDas könnte es Leuten, die Ihr Open Food Facts-Pseudonym kennen, ermöglichen:\n• Rückschlüsse auf Ihre Wohngegend ziehen\n• wissen, was Sie kaufen\nWenn Sie damit nicht einverstanden sind, ändern Sie bitte Ihr Pseudonym oder erstellen Sie ein neues Open Food Facts-Konto und melden Sie sich damit bei der App an.", + "prices_unknown_product": "Unbekanntes Produkt", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Fertig", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Serverkonfiguration", + "dev_mode_section_product_page": "Produktseite", + "dev_mode_section_ui": "Benutzeroberfläche", + "dev_mode_section_data": "Daten", + "dev_mode_section_experimental_features": "Experimentelle Funktionen", "dev_mode_hide_ecoscore_title": "Eco-Score ausschließen", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Verwenden Sie eine Rechtschreibprüfung für OCR-Bildschirme", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Zutaten und Verpackung)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Diese Suche wiederverwenden und bearbeiten", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Mein Preise", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Meine Belege", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "Meine Nachweise", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Preise der Mitwirkenden", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Neueste Preise hinzugefügt", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Aktivste Preis-Mitwirkende", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Läden mit den meisten Preisen", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Produkte mit den meisten Preisen", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Alle „noch zu vervollständigenden” Produkte", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "Der Preis wird schnellstmöglich an den Server übermittelt.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Aktualisierung aller lokal gespeicherten Produkte wird gestartet", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Wählen Sie Ihre Währung:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Sprache auswählen:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (neue Berechnung)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (neue Berechnung)", "nutriscore_unknown": "Unbekannter Nutri-Score", + "nutriscore_unknown_new_formula": "Unbekannter Nutri-Score (neue Berechnung)", "nutriscore_not_applicable": "Nutri-Score nicht zutreffend", + "nutriscore_not_applicable_new_formula": "Nutri-Score ist nicht anwendbar (neue Berechnung)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Installieren Sie Open Beauty Facts, um eine Kosmetikdatenbank zu erstellen", "faq_title_install_pet": "Installieren Sie Open Pet Food Facts, um eine Tierfutterdatenbank zu erstellen", "faq_title_install_product": "Installieren Sie Open Products Facts, um eine Produktdatenbank zur Verlängerung der Lebensdauer von Objekten zu erstellen", + "faq_nutriscore_nutriscore": "Neue Berechnung des Nutri-Score: Was ist neu?", "contact_title_pro_page": "Pro? Importieren Sie Ihre Produkte in Open Food Facts", "contact_title_pro_email": "Kontakt zum Produzenten", "contact_title_press_page": "Pressemappe", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Dieser Link kann auf Ihrem Gerät nicht geöffnet werden. Bitte überprüfen Sie, ob ein Browser installiert ist.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details für {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details für {pageName} mit {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Anleitung", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Teilen", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "Der Nutri-Score entwickelt sich weiter: Erklärungen!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "de", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Was ist der Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "Der Nutri-Score ist eine Kennzeichnung, die Sie über die **Nährwertqualität von Lebensmitteln** informieren soll.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "Der Farbcode reicht von dunkelgrün (**A**) für die **gesündesten** Produkte bis dunkelrot (**E**) für die **weniger gesunden** Produkte.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "Das Nutri-Score A-Logo", + "guide_nutriscore_v2_why_v2_title": "Warum wird Nutri-Score weiterentwickelt?", + "guide_nutriscore_v2_why_v2_intro": "Die Nutri-Score-Formel wird **weiterentwickelt**, um bessere Empfehlungen geben zu können:", + "guide_nutriscore_v2_why_v2_arg1_title": "Alle Getränke besser bewerten", + "guide_nutriscore_v2_why_v2_arg1_text": "Die Vergleichsnoten für **Milch**, **Milchgetränke** mit Zuckerzusatz und **Gemüsegetränke** wurden in dem neuen Algorithmus besser unterschieden.", + "guide_nutriscore_v2_why_v2_arg2_title": "Bessere Einstufung von Getränken", + "guide_nutriscore_v2_why_v2_arg2_text": "Der **Zuckergehalt** wird besser berücksichtigt und begünstigt **wenig gesüßte** Getränke.\\n**Süßstoffe werden ebenfalls abgewertet**: Diät-Limonaden werden von einer B-Bewertung auf eine Bewertung zwischen C und E herabgestuft. Wasser bleibt das empfohlene Getränk.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salz und Zucker werden abgewertet", + "guide_nutriscore_v2_why_v2_arg3_text": "Bei Produkten, die **zu süß** oder **zu salzig** sind, wird die **Bewertung weiter herabgestuft**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchie bei Ölen und Fischen", + "guide_nutriscore_v2_why_v2_arg4_text": "Die Bewertung von bestimmten **fetten Fischen** und **fettreichen Ölen** wird sich verbessern.", + "guide_nutriscore_v2_why_v2_arg5_title": "Die Bewertung von bestimmten **fetten Fischen** und **fettreichen Ölen** wird sich verbessern", + "guide_nutriscore_v2_why_v2_arg5_text": "Der Verzehr von **rotem Fleisch sollte eingeschränkt werden**. Aus diesem Grund wird **Geflügel vergleichsweise besser bewertet**.", + "guide_nutriscore_v2_new_logo_title": "Wie unterscheidet sich der alte Nutri-Score durch die neue Berechnung?", + "guide_nutriscore_v2_new_logo_text": "Von nun an kann das Logo einen Hinweis \"**Neue Berechnung**\" enthalten, um zu verdeutlichen, dass es sich tatsächlich um die neue Berechnung handelt.", + "guide_nutriscore_v2_new_logo_image_caption": "Das Logo des neuen Nutri-Score", + "guide_nutriscore_v2_where_title": "Wo finde ich die neue Nutri-Score-Berechnung?", + "guide_nutriscore_v2_where_paragraph1": "Der Nutri-Score wird in mehreren Ländern angewendet: Deutschland, Belgien, Spanien, Frankreich, Luxemburg, die Niederlande und die Schweiz.", + "guide_nutriscore_v2_where_paragraph2": "Die Hersteller haben bis spätestens **2026** Zeit, **die alte Berechnung durch die neue zu ersetzen**.", + "guide_nutriscore_v2_where_paragraph3": "Ohne zu warten, **finden Sie bereits in der OpenFoodFacts-Anwendung** die neue Berechnung, auch wenn die Hersteller die Punktzahl noch nicht aktualisiert haben.", + "guide_nutriscore_v2_unchanged_title": "Was sich nicht ändert", + "guide_nutriscore_v2_unchanged_paragraph1": "Der Nutri-Score ist ein Punktesystem zur **Bewertung der Nährwertqualität**. Er ist eine **Ergänzung zur NOVA-Gruppe** für **hochverarbeitete Lebensmittel** (ebenfalls in der App enthalten).", + "guide_nutriscore_v2_unchanged_paragraph2": "Für die Hersteller ist die Angabe des Nutri-Score **weiterhin optional**.", + "guide_nutriscore_v2_share_link": "https://de.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Vorschau", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_el.arb b/packages/smooth_app/lib/l10n/app_el.arb index b218c315a52..5a60a511338 100644 --- a/packages/smooth_app/lib/l10n/app_el.arb +++ b/packages/smooth_app/lib/l10n/app_el.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Μπροστινή φωτογραφία προϊόντος", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Επιβεβαίωση μεταφόρτωσης μπροστινής φωτογραφίας προϊόντος", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Φωτογραφία συστατικών", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Φωτογραφία ανακύκλωσης", + "take_photo_title": "Τραβήξτε μια φωτογραφία", "take_more_photo_title": "Τραβήξτε περισσότερες φωτογραφίες", "front_photo_uploaded": "Η μπροστινή φωτογραφία μεταφορτώθηκε", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Δεν βρέθηκε προϊόν", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "δεν βρέθηκε:", - "searchPanelHeader": "Αναζητήστε ή σαρώστε το πρώτο σας προϊόν", - "@Product query status": {}, "refreshing_product": "Ανανέωση προϊόντος", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Η ανάκτηση πληροφοριών για αυτό το προϊόν ήταν αδύνατη λόγω σφάλματος δικτύου.", "cached_results_from": "Εμφάνιση αποτελεσμάτων από:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Σχεδιάσαμε\nτη συλλογική εφαρμογή σάρωσης\nτο 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Καθώς κλείνουμε τα 10, επανασχεδιάζουμε την εφαρμογή ξανά\nαπό την αρχή!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Φόρτωση του πρώτου παραδείγματος προϊόντος σας", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Συμπληρώστε τις βασικές λεπτομέρειες", "not_implemented_snackbar_text": "Δεν έχει υλοποιηθεί ακόμη", "category_picker_page_appbar_text": "Κατηγορίες", - "edit_ingredients_extrait_ingredients_btn_text": "Εξαγωγή συστατικών", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Ανανέωση φωτογραφίας", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Ανανέωση φωτογραφίας", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Δεδομένα", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Κοινοποίηση", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 0582593abcc..72f4d0ceced 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -129,10 +129,6 @@ "@whatIsOff": { "description": "Description of Open Food Facts organization." }, - "offUtility": "Choose food that is 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." @@ -566,6 +562,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +624,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +736,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +746,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +799,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +849,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1115,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1231,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1475,50 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, + "edit_ingredients_loading_photo_help_dialog_title": "Why do I see this message?", + "@edit_ingredients_loading_photo_help_dialog_title": { + "description": "Ingredients edition - Dialog explaining why the photo is loading - Title" + }, + "edit_ingredients_loading_photo_help_dialog_body": "To use the \"Extract ingredients\" feature, the photo needs to be uploaded first.\n\nPlease wait a few seconds or enter them manually.", + "@edit_ingredients_loading_photo_help_dialog_body": { + "description": "Ingredients edition - Dialog explaining why the photo is loading - Content" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, + "edit_packaging_loading_photo_help_dialog_title": "Why do I see this message?", + "@edit_packaging_loading_photo_help_dialog_title": { + "description": "Packaging edition - Dialog explaining why the photo is loading - Title" + }, + "edit_packaging_loading_photo_help_dialog_body": "To use the \"Extract packaging\" feature, the photo needs to be uploaded first.\n\nPlease wait a few seconds or enter them manually.", + "@edit_packaging_loading_photo_help_dialog_body": { + "description": "Packaging edition - Dialog explaining why the photo is loading - Content" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1621,16 +1728,199 @@ "@dev_preferences_import_history_subtitle": { "description": "User dev preferences - Import history - Subtitle" }, + "dev_preferences_news_custom_url_title": "Custom URL for news", + "@dev_preferences_news_custom_url_title": { + "description": "News dev preferences - Custom URL for news - Title" + }, + "dev_preferences_news_custom_url_subtitle": "URL of the JSON file:", + "@dev_preferences_news_custom_url_subtitle": { + "description": "News dev preferences - Custom URL for news - Title" + }, + "dev_preferences_news_custom_url_empty_value": "Not set", + "@dev_preferences_news_custom_url_empty_value": { + "description": "Message to show when the custom news URL is not set" + }, + "dev_preferences_news_provider_status_title": "Status", + "@dev_preferences_news_provider_status_title": { + "description": "News dev preferences - Status - Title" + }, + "dev_preferences_news_provider_status_subtitle": "Last refresh: {date}", + "@dev_preferences_news_provider_status_subtitle": { + "description": "News dev preferences - Custom URL for news - Subtitle", + "placeholders": { + "date": { + "type": "String" + } + } + }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_product_accessibility_summary": "{count,plural, =1{1 price} other{{count} prices}} for {product}", + "@prices_product_accessibility_summary": { + "description": "A card summarizing the number of prices for a product", + "placeholders": { + "count": { + "type": "int" + }, + "product": { + "type": "String" + } + } + }, + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_entry_accessibility_label": "Price: {price} / Store: \"{location}\" / Published on {date} by \"{user}\"", + "@prices_entry_accessibility_label": { + "description": "Accessibility label for a price entry", + "placeholders": { + "price": { + "type": "String" + }, + "location": { + "type": "String" + }, + "date": { + "type": "String" + }, + "user": { + "type": "String" + } + } + }, + "prices_open_user_proofs": "Open proofs of \"{user}\"", + "@prices_open_user_proofs": { + "description": "Button to open the proofs of a user", + "placeholders": { + "user": { + "type": "String" + } + } + }, + "prices_open_proof": "Open price proof", + "@prices_open_proof": { + "description": "Button to open a proof" + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_news": "News provider configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1972,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1936,9 +2258,13 @@ } } }, - "capture": "Capture New", + "capture": "Take a new picture", "@capture": { - "description": "Button label for taking a photo" + "description": "Button label for taking a new photo (= there's already one)" + }, + "capture_new_picture": "Take a picture", + "@capture_new_picture": { + "description": "Button label for taking a new photo (= the first one)" }, "choose_from_gallery": "Choose from gallery", "@choose_from_gallery": { @@ -1948,6 +2274,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2792,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2822,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2856,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2873,88 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" + }, + "prices_feedback_form": "Click here to send us your feedback about this new feature!", + "@prices_feedback_form": { + "description": "A button to send feedback about the prices feature" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_eo.arb b/packages/smooth_app/lib/l10n/app_eo.arb index ac008dfd099..4b83b9f54aa 100644 --- a/packages/smooth_app/lib/l10n/app_eo.arb +++ b/packages/smooth_app/lib/l10n/app_eo.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategorioj", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_es.arb b/packages/smooth_app/lib/l10n/app_es.arb index a8668beb4b3..83aba39111e 100644 --- a/packages/smooth_app/lib/l10n/app_es.arb +++ b/packages/smooth_app/lib/l10n/app_es.arb @@ -371,7 +371,7 @@ }, "contribute_improve_text": "La base de datos es el núcleo del proyecto. Puedes ayudar de manera fácil y muy rápido. Descarga la aplicación en tu teléfono móvil y empieza a añadir productos o a mejorar los que ya están.\n\nPor otro lado, el sitio web de Open Food Facts ofrece muchas maneras de colaborar: ", "@contribute_improve_text": {}, - "contribute_translate_header": "Traducir", + "contribute_translate_header": "Traduce", "@contribute_translate_header": { "description": "Button label + pop up window title: Shows information about helping by translating" }, @@ -481,7 +481,7 @@ "description": "Top meta-entry on a category filter" }, "category_search": "(búsqueda de categorías)", - "filter": "Filtros", + "filter": "Filtro", "@filter": { "description": "A button that opens a menu where you can filter within categories. Juices => Apple juices/Orange juices" }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Una ilustración con Nutri-Score y Eco-Score desconocidos", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Foto frontal del producto", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirmar el envío de la foto frontal del producto", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Foto de los ingredientes", "nutritional_facts_photo_title": "Foto de la información nutricional", "recycling_photo_title": "Foto sobre el reciclaje", + "take_photo_title": "Saca una foto", "take_more_photo_title": "Haz más fotos", "front_photo_uploaded": "Foto frontal subida", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No se ha encontrado ningún producto", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "no se ha encontrado:", - "searchPanelHeader": "Busca o escanea tu primer producto", - "@Product query status": {}, "refreshing_product": "Actualizando producto", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Bienvenida a Open Food Facts∫", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Escanee** un código de barras o\n**busque** un producto", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Buscar un producto", + "homepage_main_card_search_field_tooltip": "Iniciar búsqueda", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "¿Te gusta la aplicación?", "tagline_app_review_button_positive": "¡Me encanta! 😍", "tagline_app_review_button_negative": "No mucho…", "tagline_app_review_button_later": "Pregúntame más tarde", + "tagline_feed_news_button": "Más información", "app_review_negative_modal_title": "¿No te gusta nuestra aplicación?", "app_review_negative_modal_text": "¿Nos podrías explicar por qué?", "app_review_negative_modal_positive_button": "¡Sí, por supuesto!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Imposible obtener información sobre este producto debido a un error de red.", "cached_results_from": "Mostrar resultados de:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Por favor, elija una moneda", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Por favor, elige un país:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Inventamos\nla aplicación de escaneo\ncolaborativa en 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Hemos cumplido 10 años\ny nos estamos reinventando\n¡por completo!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continuar", "onboarding_welcome_loading_dialog_title": "Cargando tu primer producto de ejemplo", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibración y respuesta táctil", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Completa los detalles básicos", "not_implemented_snackbar_text": "Aún sin implementar", "category_picker_page_appbar_text": "Categorías", - "edit_ingredients_extrait_ingredients_btn_text": "Extraer ingredientes", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Actualizar foto", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extraer el envase", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Actualizar foto", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1586,7 +1681,7 @@ "@dev_preferences_export_history_progress_found": { "description": "User dev preferences - Export history - Item - Found" }, - "dev_preferences_export_history_progress_not_found": "producto NO encontrado", + "dev_preferences_export_history_progress_not_found": "producto no encontrado", "@dev_preferences_export_history_progress_not_found": { "description": "User dev preferences - Export history - Item - Not found" }, @@ -1623,19 +1718,133 @@ }, "prices_app_dev_mode_flag": "Acceso directo a la aplicación Prices en la página del producto", "prices_app_button": "Ir a la aplicación Prices", + "prices_generic_title": "Precios", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Añadir un precio", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Producto no encontrado", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Búsqueda de producto", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Precio", + "prices_amount_price_discounted": "Precio con descuento", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Moneda", + "prices_date_subtitle": "Fecha", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Comprobante", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Ticket", + "prices_proof_price_tag": "Etiqueta de precio", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Producto desconocido", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Hecho", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Datos", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Excluir Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reutilizar y editar esta búsqueda", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" }, - "product_search_no_more_results": "Has descargado los {totalSize} productos.", + "product_search_no_more_results": "Has descargado todos los {totalSize} productos.", "@product_search_no_more_results": { "description": "Product search list - No more results available", "placeholders": { @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Mis precios", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Mis pruebas", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Todos los productos por completar", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Iniciando la actualización de todos los productos almacenados localmente", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2246,7 +2491,7 @@ "background_task_error_no_internet": "Error de conexión a Internet. Inténtalo más tarde.", "background_task_operation_unknown": "tipo de operación desconocida", "background_task_operation_details": "cambios detallados", - "background_task_operation_image": "carga de fotos", + "background_task_operation_image": "subir foto", "background_task_operation_refresh": "actualización retrasada después de cargar la foto", "background_task_run_started": "iniciado", "background_task_run_not_started": "todavía no se ha iniciado", @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Seleccione su moneda:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Selecciona tu idioma:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Nutri-Score desconocido", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "No es posible aplicar el Nutri-Score", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Instala Open Beauty Facts para crear una base de datos sobre cosmética", "faq_title_install_pet": "Instala Open Pet Food Facts para crear una base de datos sobre alimentación para mascotas", "faq_title_install_product": "Instala Open Products Facts para crear una base de datos sobre productos del día a día", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "¿Eres profesional? Importa tus productos a Open Food Facts", "contact_title_pro_email": "Contacto para productores", "contact_title_press_page": "Página de prensa", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Este enlace no se puede abrir en tu dispositivo. Comprueba que tienes un navegador instalado.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Compartir", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "¿Qué es el Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "¿Dónde encontrar el nuevo cálculo Nutri-Score?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/guide/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_et.arb b/packages/smooth_app/lib/l10n/app_et.arb index 757f9641b74..050e4ce7d3c 100644 --- a/packages/smooth_app/lib/l10n/app_et.arb +++ b/packages/smooth_app/lib/l10n/app_et.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Tehke foto", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategooriad", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Andmed", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_eu.arb b/packages/smooth_app/lib/l10n/app_eu.arb index b7a2b3732ff..594021bf777 100644 --- a/packages/smooth_app/lib/l10n/app_eu.arb +++ b/packages/smooth_app/lib/l10n/app_eu.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Produktuaren aurreko aldeko argazkia", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Osagaien argazkia", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Birziklapenaren argazkia", + "take_photo_title": "Atera argazki bat", "take_more_photo_title": "Atera argazki gehiago", "front_photo_uploaded": "Aurrealdeko argazkia igo da", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Ez da produkturik aurkitu", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "ez da aurkitu:", - "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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Aplikazioa gustatzen zaizu?", "tagline_app_review_button_positive": "Datsegit! 😍", "tagline_app_review_button_negative": "Egia esan ez…", "tagline_app_review_button_later": "Galdetu geroago", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Ez zaizu gure aplikazioa gustatzen?", "app_review_negative_modal_text": "Segundo batzuk har zenezake arrazoia azaltzeko?", "app_review_negative_modal_positive_button": "Bai, jakina!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategoriak", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Prezioak aplikazioko produktuaren orrirako laster-bidea", "prices_app_button": "Joan Prezioak aplikaziora", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Deskontua egindako prezioa", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Datuak", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Berrerabili eta editatu bilaketa hau", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Bertan gordeta dauden produktu guztiak freskatzeari ekiten", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Hautatu zure hizkuntza:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Ezin da ireki esteka gailuan. Egiaztatu arakatzaile bat instalatuta duzula.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Partekatu", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "eu", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_fa.arb b/packages/smooth_app/lib/l10n/app_fa.arb index 65c25f3fb75..389afe3a5b9 100644 --- a/packages/smooth_app/lib/l10n/app_fa.arb +++ b/packages/smooth_app/lib/l10n/app_fa.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "یک عکس بگیرید", "take_more_photo_title": "ثبت تصاویر بیشتر", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "هیچ محصولی یافت نشد", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "یافت نشد:", - "searchPanelHeader": "اولین محصول خود را جستجو یا اسکن کنید", - "@Product query status": {}, "refreshing_product": "Refreshing product", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "دسته‌‎بندی‎ها", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "اشتراک", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_fi.arb b/packages/smooth_app/lib/l10n/app_fi.arb index 0dcc15c71de..26cf7e07dbf 100644 --- a/packages/smooth_app/lib/l10n/app_fi.arb +++ b/packages/smooth_app/lib/l10n/app_fi.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Kuva edestä", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Vahvista edestäpäin otetun kuvan lataaminen", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Sisältöluettelon kuva", "nutritional_facts_photo_title": "Ravintosisältökuva", "recycling_photo_title": "Kierrätyksen kuva", + "take_photo_title": "Ota kuva", "take_more_photo_title": "Ota lisää kuvia", "front_photo_uploaded": "Etukuva ladattu", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Tuotetta ei löytynyt", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "ei löytynyt:", - "searchPanelHeader": "Hae tai skannaa ensimmäinen tuotteesi", - "@Product query status": {}, "refreshing_product": "Päivitetään tuotetta", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Etsi tuote", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Pidätkö sovelluksesta?", "tagline_app_review_button_positive": "Rakastan sitä! 😍", "tagline_app_review_button_negative": "En juurikaan…", "tagline_app_review_button_later": "Kysy myöhemmin", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Etkö pidä sovelluksestamme?", "app_review_negative_modal_text": "Voisitko käyttää muutaman sekunnin kertoaksesi meille miksi?", "app_review_negative_modal_positive_button": "Kyllä, ehdottomasti!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Tietojen lataus epäonnistui verkkovirheen vuoksi.", "cached_results_from": "Näytä tulokset ajalta:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Valitse valuutta", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Valitse maa:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Teimme\nyhteisöllisen\nlukijasovelluksen vuonna 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Täyttäessämme 10\nuudistamme sitä\nalusta alkaen.", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Jatka", "onboarding_welcome_loading_dialog_title": "Ladataan ensimmäistä esimerkkituotettasi", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Värinä ja haptiikka", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Täytä perustiedot", "not_implemented_snackbar_text": "Ei vielä käytössä", "category_picker_page_appbar_text": "Tuoteluokat", - "edit_ingredients_extrait_ingredients_btn_text": "Poimi ainesosat", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Päivitä kuva", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Pura pakkaus", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Päivitä kuva", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Pikakuvake Hinnat-sovellukseen tuotesivulla", "prices_app_button": "Siirry Hinnat-sovellukseen", + "prices_generic_title": "Hinnat", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Lisää hinta", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Tuotetta ei löytynyt", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Tuotehaku", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Hinta", + "prices_amount_price_discounted": "Alennettu hinta", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Valuutta", + "prices_date_subtitle": "Päivämäärä", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Todiste", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Kuitti", + "prices_proof_price_tag": "Hintalappu", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Tuntematon tuote", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Valmis", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Tiedot", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Jätä ympäristöystävällisyyspisteytys huomiotta", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Käytä ja muokkaa tätä hakua uudelleen", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Minun hinnat", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Minun todisteeni", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Kaikki tehtävälistan tuotteet", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Aloitetaan kaikkien paikallisesti tallennettujen tuotteiden päivitys", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Valitse valuutta:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Valitse kieli:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Tuntematon Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score ei sovellu", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Luo kosmeettinen tietokanta asentamalla Open Beauty Facts", "faq_title_install_pet": "Luo lemmikkieläinten ruokatietokanta asentamalla Open Pet Food Facts", "faq_title_install_product": "Asenna Open Products Facts luodaksesi tuotetietokannan objektien käyttöiän pidentämiseksi", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Tuo tuotteesi Open Food Factsiin", "contact_title_pro_email": "Tuottajan yhteystiedot", "contact_title_press_page": "Lehdistösivu", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Tätä linkkiä ei voi avata laitteellasi. Tarkista, että sinulla on selain asennettuna.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Jaa", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "fi", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Mikä on Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_fo.arb b/packages/smooth_app/lib/l10n/app_fo.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_fo.arb +++ b/packages/smooth_app/lib/l10n/app_fo.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_fr.arb b/packages/smooth_app/lib/l10n/app_fr.arb index 6921f0feeb3..4e9ae8dd5b9 100644 --- a/packages/smooth_app/lib/l10n/app_fr.arb +++ b/packages/smooth_app/lib/l10n/app_fr.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Une illustration avec un Nutri-Score et un Eco-Score inconnus", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Photo frontale du produit", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirmer le téleversement de la photo frontale du produit", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Photo des ingrédients", "nutritional_facts_photo_title": "Photo des valeurs nutritionnelles", "recycling_photo_title": "Photo du recyclage", + "take_photo_title": "Prendre une photo", "take_more_photo_title": "Prendre plus de photos", "front_photo_uploaded": "Photo de la face avant téléchargée", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Aucun produit trouvé", "@no_product_found": {}, + "no_location_found": "Aucun magasin trouvé", "not_found": "non trouvé :", - "searchPanelHeader": "Recherchez ou scannez votre premier produit", - "@Product query status": {}, "refreshing_product": "Actualisation du produit", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image prise le {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image prise le {date}. Cette image est peut-être obsolète", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Bienvenue sur Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scannez** un code-barres ou\n**recherchez** un produit", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Chercher un produit", + "homepage_main_card_search_field_tooltip": "Démarrer la recherche", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Aimez-vous cette appli ?", "tagline_app_review_button_positive": "Je l'adore! 😍", "tagline_app_review_button_negative": "Pas vraiment…", "tagline_app_review_button_later": "Me demander plus tard", + "tagline_feed_news_button": "En savoir plus", "app_review_negative_modal_title": "Vous n'aimez pas notre application ?", "app_review_negative_modal_text": "Pourriez-vous prendre quelques secondes pour nous dire pourquoi ?", "app_review_negative_modal_positive_button": "Oui, absolument !", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "Une erreur est survenue !", + "product_internet_error_modal_message": "Nous ne parvenons pas à récupérer des informations sur ce produit en raison d'une erreur réseau. Veuillez vérifier votre connexion internet et réessayer.\n\nErreur interne :\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible de récupérer les informations sur ce produit en raison d'une erreur réseau.", "cached_results_from": "Résultats mis en cache depuis :", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Veuillez choisir une devise", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Vous venez de changer de pays.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Voulez-vous changer la devise de {previousCurrency} à {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Veuillez sélectionner un pays :", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Nous avons inventé\nl'appli de scan\ncollaborative en 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "10 ans après,\nnous la réinventons\ncomplètement !", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Bienvenue !", + "onboarding_home_welcome_text2": "L'application qui vous aide à choisir de la nourriture qui est bonne pour **vous** et la **planète** !", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continuer", "onboarding_welcome_loading_dialog_title": "Chargement de votre premier exemple de produit", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration et retours tactiles", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,10 +1479,18 @@ "completed_basic_details_btn_text": "Compléter les informations de base", "not_implemented_snackbar_text": "Pas encore disponible", "category_picker_page_appbar_text": "Catégories", - "edit_ingredients_extrait_ingredients_btn_text": "Extraire les ingrédients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extraire les ingrédients à partir de la photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extraction des ingrédients à partir de la photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Chargement de la photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Mettre à jour la photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" @@ -1412,6 +1499,14 @@ "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extraction de l'emballage à partir de la photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Chargement de la photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Mettre à jour la photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Raccourci vers l'application Prix sur la page produit", "prices_app_button": "Accéder à l'application Prix", + "prices_generic_title": "Prix", + "prices_add_n_prices": "{count,plural, =1{Ajouter un prix} other{Ajouter {count} prix}}", + "prices_send_n_prices": "{count,plural, =1{Envoyer le prix} other{Envoyer {count} prix}}", + "prices_add_an_item": "Ajouter un article", + "prices_add_a_price": "Ajouter un prix", + "prices_add_a_receipt": "Ajouter un ticket de caisse", + "prices_add_price_tags": "Ajouter des étiquettes de prix", + "prices_barcode_search_not_found": "Produit non trouvé", + "prices_barcode_search_none_yet": "Pas encore de produit", + "prices_barcode_search_question": "Voulez-vous chercher ce produit ?", + "prices_barcode_search_title": "Recherche de produit", + "prices_barcode_search_running": "À la recherche de {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Lecteur de code-barres", + "prices_view_prices": "Voir les prix", + "prices_list_length_one_page": "{count,plural, =0{Aucun prix} =1{Un seul prix} other{Tous les {count} prix}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "{pageSize} prix les plus récents (total : {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{Aucune preuve} =1{Une seule preuve} other{Toutes les {count} preuves}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "{pageSize} preuves les plus récentes (total : {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributeurs (total : {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Montant", + "prices_amount_is_discounted": "En promotion ?", + "prices_amount_price_normal": "Prix", + "prices_amount_price_discounted": "Prix en promo", + "prices_amount_price_not_discounted": "Prix d'origine", + "prices_amount_no_product": "Il manque un produit !", + "prices_amount_price_incorrect": "Valeur incorrecte", + "prices_amount_price_mandatory": "Valeur obligatoire", + "prices_currency_subtitle": "Devise", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Magasin", + "prices_location_find": "Chercher un magasin", + "prices_location_mandatory": "Vous devez choisir un magasin !", + "prices_proof_subtitle": "Preuve", + "prices_proof_find": "Choisir une preuve", + "prices_proof_receipt": "Ticket de caisse", + "prices_proof_price_tag": "Étiquette de prix", + "prices_proof_mandatory": "Vous devez choisir une preuve !", + "prices_add_validation_error": "Erreur de validation", + "prices_privacy_warning_title": "Avertissement de confidentialité", + "prices_privacy_warning_message": "Les prix seront publics, ainsi que le magasin auquel ils font référence.\nCela pourrait permettre aux personnes qui connaissent votre pseudonyme Open Food Facts de :\n* déduire dans quelle région vous habitez\n* savoir ce que vous achetez\nSi vous êtes pas à l'aise avec cela, veuillez changer votre pseudonyme ou créer un nouveau compte Open Food Facts et vous connecter à l'application avec celui-ci.", + "prices_unknown_product": "Produit inconnu", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Fait", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Configuration du serveur", + "dev_mode_section_product_page": "Page produit", + "dev_mode_section_ui": "Interface utilisateur", + "dev_mode_section_data": "Données", + "dev_mode_section_experimental_features": "Fonctionnalités expérimentales", "dev_mode_hide_ecoscore_title": "Exclure l'éco-score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Réutiliser et modifier cette recherche", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Mes prix", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Mes preuves", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "Ma preuve", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Prix d'un contributeur", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Derniers prix ajoutés", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top contributeurs", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Top lieux", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Top produits", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Tous les produits à compléter", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "Le prix sera envoyé en arrière-plan dès que possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Démarrage de la mise à jour de tous les produits stockés localement", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Sélectionnez votre devise:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Sélectionnez votre langue :", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (Nouveau calcul)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (nouveau calcul)", "nutriscore_unknown": "Nutri-Score inconnu", + "nutriscore_unknown_new_formula": "Nutri-Score inconnu (Nouveau calcul)", "nutriscore_not_applicable": "Nutri-Score non applicable", + "nutriscore_not_applicable_new_formula": "Le Nutri-Score n'est pas applicable (Nouveau calcul)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Éco-Score A", "ecoscore_b": "Éco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Installez Open Beauty Facts pour créer une base de données cosmétiques", "faq_title_install_pet": "Installez Open Pet Food Facts pour créer une base de données sur les aliments pour animaux de compagnie", "faq_title_install_product": "Installez Open Products Facts pour créer une base de données de produits afin de prolonger la durée de vie des objets", + "faq_nutriscore_nutriscore": "Nouveau calcul du Nutri-Score : qu'est-ce qui a changé ?", "contact_title_pro_page": "Pro? Importez vos produits dans Open Food Facts", "contact_title_pro_email": "Contact producteur", "contact_title_press_page": "Page de presse", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Ce lien ne peut pas être ouvert sur votre appareil. Veuillez vérifier qu'un navigateur est installé.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Détails pour {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Détails pour {pageName} avec {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Partager", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "Le Nutri-Score change : explications !", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "fr", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Qu'est ce que le Nutri-Score ?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "Le Nutri-Score est un logo qui a pour objectif de vous informer sur la **qualité nutritionnelle des aliments**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "Le code couleur varie en allant du vert foncé (**A**) pour les produits les **plus sains** au rouge foncé (**E**) pour les **moins sains**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "Le logo Nutri-Score A", + "guide_nutriscore_v2_why_v2_title": "Pourquoi le Nutri-Score change-t-il ?", + "guide_nutriscore_v2_why_v2_intro": "La formule du Nutri-Score **évolue** pour fournir de meilleures recommandations :", + "guide_nutriscore_v2_why_v2_arg1_title": "Mieux évaluer l'ensemble des boissons", + "guide_nutriscore_v2_why_v2_arg1_text": "Les notes comparées du **lait**, les **boissons lactées** avec ajout de sucre et **végétales** ont été mieux différenciées dans le nouvel algorithme.", + "guide_nutriscore_v2_why_v2_arg2_title": "Un meilleur classement des boissons", + "guide_nutriscore_v2_why_v2_arg2_text": "La **teneur en sucres** est mieux prise en compte et avantage les boissons **faiblement sucrées**.\\nLes **édulcorants seront également pénalisés** : les sodas light vont être déclassés pour passer d’une note B à entre C et E. L’eau reste la boisson recommandée.", + "guide_nutriscore_v2_why_v2_arg3_title": "Le sel et sucre pénalisés", + "guide_nutriscore_v2_why_v2_arg3_text": "Les produits **trop sucrés** ou **trop salés** verront leur **note encore dégradée**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hiérarchisation des huiles et poissons", + "guide_nutriscore_v2_why_v2_arg4_text": "La note de certains **poissons gras** et des **huiles riches en bonnes graisses** va s’améliorer.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limiter la viande rouge", + "guide_nutriscore_v2_why_v2_arg5_text": "La consommation de **viande rouge est à limiter**. C’est pourquoi la **volaille sera comparativement mieux classée**.", + "guide_nutriscore_v2_new_logo_title": "Comment différencier ancien Nutri-Score et nouveau calcul ?", + "guide_nutriscore_v2_new_logo_text": "Désormais le logo peut arborer une mention \"**Nouveau calcul**\" pour clarifier qu'il s'agit bien du nouveau calcul.", + "guide_nutriscore_v2_new_logo_image_caption": "Le logo du nouveau Nutri-Score", + "guide_nutriscore_v2_where_title": "Où trouver le nouveau calcul du Nutri-Score ?", + "guide_nutriscore_v2_where_paragraph1": "Le Nutri-Score est en application dans plusieurs pays : l'Allemagne, la Belgique, l'Espagne, la France, le Luxembourg, les Pays-Bas et la Suisse.", + "guide_nutriscore_v2_where_paragraph2": "Les fabricants ont jusqu'en **2026** au plus tard **pour remplacer** l'ancien calcul par le nouveau.", + "guide_nutriscore_v2_where_paragraph3": "Sans attendre, vous **retrouvez déjà dans l'application Open Food Facts**, le nouveau calcul, y compris si les fabriquants n’ont pas mis à jour le score.", + "guide_nutriscore_v2_unchanged_title": "Ce qui ne change pas", + "guide_nutriscore_v2_unchanged_paragraph1": "Le Nutri-Score est un score conçu pour **mesurer la qualité nutritionnelle**. Il est **complémentaire au groupe NOVA** sur les **aliments ultra-transformés** (également présent dans l'application).", + "guide_nutriscore_v2_unchanged_paragraph2": "Pour les fabricants, l'affichage du Nutri-Score **reste optionnel**.", + "guide_nutriscore_v2_share_link": "https://fr.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Aperçu", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ga.arb b/packages/smooth_app/lib/l10n/app_ga.arb index 9578c2b338b..326ba962b9a 100644 --- a/packages/smooth_app/lib/l10n/app_ga.arb +++ b/packages/smooth_app/lib/l10n/app_ga.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Tóg pictiúr", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Catagóirí", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Comhroinn", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_gd.arb b/packages/smooth_app/lib/l10n/app_gd.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_gd.arb +++ b/packages/smooth_app/lib/l10n/app_gd.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_gl.arb b/packages/smooth_app/lib/l10n/app_gl.arb index 9411e2c8ff6..820ec0df6e8 100644 --- a/packages/smooth_app/lib/l10n/app_gl.arb +++ b/packages/smooth_app/lib/l10n/app_gl.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Tirar unha foto", "take_more_photo_title": "Tomar máis fotografías", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categorías", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Datos", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Compartir", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_gu.arb b/packages/smooth_app/lib/l10n/app_gu.arb index 403a45828b1..1ab5d8c7a32 100644 --- a/packages/smooth_app/lib/l10n/app_gu.arb +++ b/packages/smooth_app/lib/l10n/app_gu.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "ફોટો પાડો", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "શ્રેણીઓ", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "માહિતી", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ha.arb b/packages/smooth_app/lib/l10n/app_ha.arb index 837806e0462..26d87af487c 100644 --- a/packages/smooth_app/lib/l10n/app_ha.arb +++ b/packages/smooth_app/lib/l10n/app_ha.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_he.arb b/packages/smooth_app/lib/l10n/app_he.arb index 802b06c8441..c4ea97f5154 100644 --- a/packages/smooth_app/lib/l10n/app_he.arb +++ b/packages/smooth_app/lib/l10n/app_he.arb @@ -332,7 +332,7 @@ "@legalNotices": { "description": "A link to open the legal notices on the website" }, - "privacy_policy": "Privacy policy", + "privacy_policy": "מדיניות פרטיות", "@privacy_policy": { "description": "A link to open the privacy policy on the website" }, @@ -485,7 +485,7 @@ "@filter": { "description": "A button that opens a menu where you can filter within categories. Juices => Apple juices/Orange juices" }, - "scan": "Products from the Scan screen", + "scan": "מוצרים ממסך הסריקה", "@scan": { "description": "Page title: List type: Products in the scan session" }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "איור עם ציוני Nutri-Score ו־Eco-Score לא ידועים", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "תמונת החזית של המוצר", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "לאשר את העלאת תמונת החזית של המוצר", @@ -624,6 +628,7 @@ "ingredients_photo_title": "תמונת רכיבים", "nutritional_facts_photo_title": "תמונת פירוט תזונתי", "recycling_photo_title": "תמונת מיחזור", + "take_photo_title": "צילום תמונה", "take_more_photo_title": "צילום תמונות נוספות", "front_photo_uploaded": "תמונת החזית נשלחה", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "לא נמצאו מוצרים", "@no_product_found": {}, + "no_location_found": "לא נמצא מקום", "not_found": "לא נמצאו:", - "searchPanelHeader": "כדאי לך לחפש או לסרוק את המוצר הראשון שלך", - "@Product query status": {}, "refreshing_product": "המוצר מתרענן", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "התמונה צולמה ב־{date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "התמונה צולמה ב־{date}. היא כנראה לא עדכנית", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "ברוך בואך ל־Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "ניתן **לסרוק** ברקוד או\n**לחפש** מוצר", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "חיפוש מוצר", + "homepage_main_card_search_field_tooltip": "התחלת חיפוש", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "איך היישומון שלנו?", "tagline_app_review_button_positive": "הוא מעולה! 😍", "tagline_app_review_button_negative": "ככה ככה…", "tagline_app_review_button_later": "אחר כך", + "tagline_feed_news_button": "מידע נוסף", "app_review_negative_modal_title": "לא אהבת את היישומון שלנו?", "app_review_negative_modal_text": "נוכל לבקש ממך להקדיש מספר שניות ולספר לנו למה?", "app_review_negative_modal_positive_button": "כן, כמובן!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "לא ניתן למשוך מידע על המוצר הזה עקב שגיאת רשת.", "cached_results_from": "להציג תוצאות מ־:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "נא לבחור מטבע", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "הרגע החלפת בין מדינות.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "להחליף מהמטבע {previousCurrency} למטבע {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "נא לבחור מדינה:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "המצאנו\nאת יישומון\nהסריקה השיתופי ב־2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "לרגל יום ההולדת\nהעשירי אנחנו מחדשים\nאותו לגמרי מאפס!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "להמשיך", "onboarding_welcome_loading_dialog_title": "מוצר ההדגמה הראשוני שלך נטען", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "רטט ומשוב פיזי", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "השלמת הפרטים הבסיסיים", "not_implemented_snackbar_text": "לא מיושם עדיין", "category_picker_page_appbar_text": "קטגוריות", - "edit_ingredients_extrait_ingredients_btn_text": "חילוץ רכיבים", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "רענון תמונה", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "חילוץ אריזה", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "רענון תמונה", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "קיצור דרך ליישומון מחירים בעמוד המוצר", "prices_app_button": "מעבר ליישומון המחירים", + "prices_generic_title": "מחירים", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "הוספת מחיר", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "המוצר לא נמצא", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "חיפוש מוצרים", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, two {כל {count} המחירים} many {כל {count} המחירים}=0{אין מחיר עדיין} =1{מחיר אחד בלבד} other{כל {count} המחירים}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "{pageSize} המחירים העדכניים ביותר (סך הכול: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "{pageSize} המתנדבים המובילים (בסך הכול: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "סכום", + "prices_amount_is_discounted": "בהנחה?", + "prices_amount_price_normal": "מחיר", + "prices_amount_price_discounted": "מחיר מוזל", + "prices_amount_price_not_discounted": "מחיר מקורי", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "ערך שגוי", + "prices_amount_price_mandatory": "ערך חובה", + "prices_currency_subtitle": "מטבע", + "prices_date_subtitle": "תאריך", + "prices_location_subtitle": "חנות", + "prices_location_find": "איתור חנות", + "prices_location_mandatory": "חובה לבחור חנות!", + "prices_proof_subtitle": "הוכחה", + "prices_proof_find": "בחירת הוכחה", + "prices_proof_receipt": "קבלה", + "prices_proof_price_tag": "תווית מחיר", + "prices_proof_mandatory": "חובה לבחור הוכחה!", + "prices_add_validation_error": "שגיאת אימות", + "prices_privacy_warning_title": "אזהרת פרטיות", + "prices_privacy_warning_message": "המחירים יהיו גלויים לציבור, לצד החנויות שקשורות אליהם.\nכך יכולים אנשים שמכירים את הכינוי שלך ב־Open Food Facts:\n* להסיק מה אזור המגורים שלך\n* לדעת מה בסל הקניות שלך\nאם לא נוח לך לחשוף מידע שכזה, נא לשנות את הכינוי שלך או ליצור חשבון חדש ב־Open Food Facts ולהיכנס אליו ביישומון.", + "prices_unknown_product": "מוצר לא ידוע", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "בוצע", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "נתונים", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "להחריג Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "שימוש בחיפוש ועריכתו", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "המחירים שלי", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "ההוכחות שלי", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "מחירי מתנדבים", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "המחירים האחרים שנוספו", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "תורמי המחירים המובילים", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "חנויות עם הכי הרבה מחירים", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "מוצרים עם הכי הרבה מחירים", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "כל המוצרים להשלמה", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "המחיר יישלח לשרת במהירות האפשרית.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "מתחיל רענון של כל המוצרים שמאוחסנים מקומית", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "בחירת המטבע שלך:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "נא לבחור את השפה שלך:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (נוסחה חדשה)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (נוסחה חדשה)", "nutriscore_unknown": "Nutri-Score לא ידוע", + "nutriscore_unknown_new_formula": "Nutri-Score לא ידוע (נוסחה חדשה)", "nutriscore_not_applicable": "Nutri-Score לא ניתן ליישום", + "nutriscore_not_applicable_new_formula": "Nutri-Score לא תקף (נוסחה חדשה)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,12 +2771,13 @@ "faq_title_install_beauty": "אפשר להתקין את Open Beauty Facts כדי ליצור מסד נתונים קוסמטי", "faq_title_install_pet": "אפשר להתקין את Open Pet Food Facts כדי ליצור מסד נתוני מזון לחיות מחמד", "faq_title_install_product": "אפשר להתקין את Open Products Facts כדי ליצור מסד נתונים של מוצרים להארכת חיי הפריטים", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "יצרנים? אפשר לייבא את המוצרים שלכם ל־Open Food Facts", "contact_title_pro_email": "איש קשר ליצרנים", "contact_title_press_page": "עמוד לעיתונאים", "contact_title_press_email": "יצירת קשר לעיתונאים", "contact_title_newsletter": "הרשמה לרשימת התפוצה שלנו", - "hunger_games_loading_line1": "Please give us a few seconds…", + "hunger_games_loading_line1": "נא להמתין מספר שניות…", "hunger_games_loading_line2": "אנו מורידים את השאלות!", "hunger_games_error_label": "געוואלד! משהו השתבש… לא הצלחנו לטעון את השאלות.", "hunger_games_error_retry_button": "מומלץ לנסות שוב!", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "אי אפשר לפתוח את הקישור הזה במכשיר שלך. נא לבדוק שיש לך דפדפן מותקן.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "פרטים על {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "פרטים על {pageName} עם {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "מדריך", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "שיתוף", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "Nutri-Score מתפתח: הסברים!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "he", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "מה זה Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "ה־Nutri-Score הוא לוגו שמטרתו ליידע אותך על **האיכות התזונתית של מוצרי המזון**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "קוד הצבע משתנה מירוק כהה (**A**) למוצרים **הבריאים** ביותר ועד אדום כהה (**E**) לאלו **שפחות בריאים**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "הלוגו Nutri-Score A", + "guide_nutriscore_v2_why_v2_title": "למה Nutri-Score מתפתח?", + "guide_nutriscore_v2_why_v2_intro": "נוסחת Nutri-Score **מתפתחת** כדי לספק המלצות טובות יותר:", + "guide_nutriscore_v2_why_v2_arg1_title": "הערכה טובה יותר של משקאות", + "guide_nutriscore_v2_why_v2_arg1_text": "הערות ההשוואה של **חלב**, **משקאות חלב** בתוספת סוכר ומשקאות **צמחיים** יהיו ברורים יותר באלגוריתם החדש.", + "guide_nutriscore_v2_why_v2_arg2_title": "דירוג טוב יותר של משקאות", + "guide_nutriscore_v2_why_v2_arg2_text": "**תכולת הסוכר** תילקח בחשבון בצורה יותר משמעותית ותינתן העדפה למשקאות **פחות ממותקים**.\\n**ענישה לממתיקים מלאכותיים**: משקאות דיאטטים מוגזים שונמכו מדירוג B לאיפשהו בין C ל־E. מים הם עדיין המשקה המומלץ.", + "guide_nutriscore_v2_why_v2_arg3_title": "מלח וסוכר נענשים", + "guide_nutriscore_v2_why_v2_arg3_text": "מוצרים **מתוקים מדי** או **מלוחים מדי** יזכו **לדירוג נחות עוד יותר**.", + "guide_nutriscore_v2_why_v2_arg4_title": "היררכיה בתוך שמנים ודגים", + "guide_nutriscore_v2_why_v2_arg4_text": "דירוג סוגים מסוימים של **דגים שמנים** ו**שמנים עשירים בשומנים טובים** ישתפר.", + "guide_nutriscore_v2_why_v2_arg5_title": "הגבלת בשר אדום", + "guide_nutriscore_v2_why_v2_arg5_text": "צריכת **בשר אדום אמורה להיות מוגבלת**. לכן **עוף יזכה לדירוג טוב יותר בהשוואה**.", + "guide_nutriscore_v2_new_logo_title": "כיצד להבדיל בין Nutri-Score הישן לבין הנוסחה החדשה?", + "guide_nutriscore_v2_new_logo_text": "מעתה, הלוגו יציג אזכור „**נוסחה חדשה**” (New calculation) כדי להבהיר שאכן זו הנוסחה החדשה.", + "guide_nutriscore_v2_new_logo_image_caption": "הלוגו של Nutri-Score החדש", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "Nutri-Score מיושם במספר מדינות: גרמניה, בלגיה, ספרד, צרפת, לוקסמבורג, הולנד ושווייץ.", + "guide_nutriscore_v2_where_paragraph2": "ליצרנים יש עד **2026** לכל המאוחר **להחליף** את החישוב הישן בחדש.", + "guide_nutriscore_v2_where_paragraph3": "מבלי להמתין, כבר אפשר למצוא את הנוסחה החדשה ב**יישומון של Open Food Facts**ֿ גם אם היצרנים טרם עדכנו את הניקוד.", + "guide_nutriscore_v2_unchanged_title": "מה לא משתנה", + "guide_nutriscore_v2_unchanged_paragraph1": "Nutri-Score הוא ציון שנועד **למדוד איכות תזונתית**. הוא **משלים לקבוצת NOVA** על **מזון אולטרה מעובד** (קיים גם ביישום).", + "guide_nutriscore_v2_unchanged_paragraph2": "ליצרנים, התצוגה של Nutri-Score **נותרת בגדר רשות**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_hi.arb b/packages/smooth_app/lib/l10n/app_hi.arb index 4235aa0e298..a1f35fb3f93 100644 --- a/packages/smooth_app/lib/l10n/app_hi.arb +++ b/packages/smooth_app/lib/l10n/app_hi.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "एक तस्वीर ले लो", "take_more_photo_title": "और तस्वीरें लें", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "श्रेणियाँ", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "एक मूल्य जोड़े", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "कीमत", + "prices_amount_price_discounted": "छूट की दर", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "सबूत", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "रसीद", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "जानकारी", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "शेयर", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_hr.arb b/packages/smooth_app/lib/l10n/app_hr.arb index 7c42b22244b..0c4bb37ef9e 100644 --- a/packages/smooth_app/lib/l10n/app_hr.arb +++ b/packages/smooth_app/lib/l10n/app_hr.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Uslikaj", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategorije", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Podaci", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ht.arb b/packages/smooth_app/lib/l10n/app_ht.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_ht.arb +++ b/packages/smooth_app/lib/l10n/app_ht.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_hu.arb b/packages/smooth_app/lib/l10n/app_hu.arb index d3946153c66..9854b5b93cb 100644 --- a/packages/smooth_app/lib/l10n/app_hu.arb +++ b/packages/smooth_app/lib/l10n/app_hu.arb @@ -199,7 +199,7 @@ "description": "Forgot password page title" }, "reset_password_explanation_text": "Elfelejtett jelszó esetén adja meg felhasználónevét vagy e-mail címét, hogy megkapja a jelszó visszaállítására vonatkozó utasításokat. Ne felejtse el ellenőrizni a Spam mappát is.", - "username_or_email": "Felhasználónév vagy e-mail", + "username_or_email": "Felhasználónév vagy e-mail cím", "@username_or_email": { "description": "Text field hint for password reset" }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Készítsen képet", "take_more_photo_title": "Készítsen további képeket", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Nem található termék", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "nem található:", - "searchPanelHeader": "Keresse meg vagy szkennelje első termékét", - "@Product query status": {}, "refreshing_product": "Termék frissítése", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Hálózati hiba miatt nem lehet információkat lekérni erről a termékről.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1070,7 +1145,7 @@ "@product_list_empty_title": { "description": "When the history list is empty, title of the message explaining to start scanning" }, - "product_list_empty_message": "Scanned products will appear here and you can check detailed information about them", + "product_list_empty_message": "A beolvasott termékek itt fognak megjelenni és megtekintheti a velük kapcsolatos részleteket", "@product_list_empty_message": { "description": "When the history list is empty, body of the message explaining to start scanning" }, @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategóriák", - "edit_ingredients_extrait_ingredients_btn_text": "Összetevők kiolvasása", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1424,7 +1519,7 @@ "@user_list_dialog_new_title": { "description": "Title of the 'new user list' dialog" }, - "user_list_dialog_rename_title": "Rename list", + "user_list_dialog_rename_title": "Lista átnevezése", "@user_list_dialog_rename_title": { "description": "Title of the 'rename user list' dialog" }, @@ -1594,7 +1689,7 @@ "@dev_preferences_export_history_dialog_title": { "description": "User dev preferences - Export history - Dialog title" }, - "dev_preferences_button_positive": "OK", + "dev_preferences_button_positive": "Ok", "@dev_preferences_button_positive": { "description": "User dev preferences - Positive button label" }, @@ -1614,7 +1709,7 @@ "dev_preferences_migration_status_already_done": "success or fresh install", "dev_preferences_migration_status_success": "success", "dev_preferences_migration_status_error": "error", - "dev_preferences_migration_status_in_progress": "in progress", + "dev_preferences_migration_status_in_progress": "folyamatban", "dev_preferences_migration_status_required": "required (click to start)", "dev_preferences_migration_status_not_started": "unknown", "dev_preferences_import_history_subtitle": "Will clear history and put 3 products in there", @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Kész", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Adatok", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1814,7 +2055,7 @@ } } }, - "confirm_delete_user_list_title": "Delete the list?", + "confirm_delete_user_list_title": "Törli a listát?", "@confirm_delete_user_list_title": { "description": "Title when asking about whether to delete the list or not" }, @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -1986,7 +2231,7 @@ "@paste_from_clipboard": { "description": "Paste the content of the clipboard" }, - "no_data_available_in_clipboard": "No data available in your clipboard", + "no_data_available_in_clipboard": "Nincs adat a vágólapon", "@no_data_available_in_clipboard": { "description": "No data available in your clipboard" }, @@ -2106,11 +2351,11 @@ "@offline_data": { "description": "App bar title for the offline data page" }, - "ocr_image_upload_instruction": "Upload an image to automatically extract the information it contains.", + "ocr_image_upload_instruction": "Töltsön fel egy képet, hogy automatikusan kinyerje a benne lévő információt.", "@ocr_image_upload_instruction": { "description": "Message shown when there is no image on the OCR extraction page for ingredients or recycling instructions" }, - "upload_image": "Upload Photo", + "upload_image": "Fénykép feltöltése", "@upload_image": { "description": "Message shown on asking to upload image" }, @@ -2243,7 +2488,7 @@ "background_task_subtitle": "Your contributions are automatically saved to our server, but not always in real-time.", "background_task_list_empty": "No Pending Background Tasks", "background_task_error_server_time_out": "Server timeout", - "background_task_error_no_internet": "Internet connection error. Try later.", + "background_task_error_no_internet": "Internetkapcsolati hiba. Próbálja újra.", "background_task_operation_unknown": "unknown operation type", "background_task_operation_details": "detailed changes", "background_task_operation_image": "photo upload", @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Megosztás", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_hy.arb b/packages/smooth_app/lib/l10n/app_hy.arb index bae0260c872..d7770356fdc 100644 --- a/packages/smooth_app/lib/l10n/app_hy.arb +++ b/packages/smooth_app/lib/l10n/app_hy.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Կիսվել", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_id.arb b/packages/smooth_app/lib/l10n/app_id.arb index 00746f08879..96d58d02f4d 100644 --- a/packages/smooth_app/lib/l10n/app_id.arb +++ b/packages/smooth_app/lib/l10n/app_id.arb @@ -332,7 +332,7 @@ "@legalNotices": { "description": "A link to open the legal notices on the website" }, - "privacy_policy": "Privacy policy", + "privacy_policy": "Kebijakan privasi", "@privacy_policy": { "description": "A link to open the privacy policy on the website" }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Ilustrasi untuk Nutri-Score dan Eco-Score yang tidak diketahui", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Foto depan produk", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Konfirmasi pengunggahan foto depan produk", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Foto Komposisi", "nutritional_facts_photo_title": "Foto Informasi Nilai Gizi", "recycling_photo_title": "Foto daur ulang", + "take_photo_title": "Ambil gambar", "take_more_photo_title": "Ambil lebih banyak foto", "front_photo_uploaded": "Foto depan diunggah", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Produk tidak ditemukan", "@no_product_found": {}, + "no_location_found": "Lokasi tidak ditemukan", "not_found": "tidak ditemukan:", - "searchPanelHeader": "Cari atau pindai produk pertama Anda", - "@Product query status": {}, "refreshing_product": "Muat ulang produk", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Cari produk", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Suka dengan aplikasinya?", "tagline_app_review_button_positive": "Saya suka sekali! 😍", "tagline_app_review_button_negative": "Tidak begitu…", "tagline_app_review_button_later": "Tanya lagi nanti", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Anda tidak menyukai aplikasi kami?", "app_review_negative_modal_text": "Bisakah Anda meluangkan waktu beberapa detik untuk memberi tahu kami alasannya?", "app_review_negative_modal_positive_button": "Ya, tentu saja!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "Terjadi kesalahan!", + "product_internet_error_modal_message": "Kami tidak dapat memperoleh informasi terkait produk ini karena kesalahan jaringan. Silakan periksa koneksi internet Anda dan coba lagi.\n\nKesalahan internal:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Pengambilan informasi tentang produk ini gagal karena kesalahan jaringan.", "cached_results_from": "Menampilkan hasil dari:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Silahkan pilih mata uang", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Anda baru saja mengganti negara.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Apakah Anda ingin mengganti mata uang dari {previousCurrency} ke {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Silakan pilih negara:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Kami membuat\naplikasi pemindaian\nkolaboratif ini pada tahun 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Saat kami berusia 10 tahun,\nkami menciptakannya lagi\ndari awal!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Selanjutnya", "onboarding_welcome_loading_dialog_title": "Memuat produk contoh pertama Anda", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Getaran & Haptik", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Lengkapi informasi dasar", "not_implemented_snackbar_text": "Belum diimplementasi", "category_picker_page_appbar_text": "Kategori", - "edit_ingredients_extrait_ingredients_btn_text": "Ekstrak kandungan bahan", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Muat ulang foto", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Kemasan ekstrak", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Muat ulang foto", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Pintasan ke aplikasi Harga di halaman produk", "prices_app_button": "Buka aplikasi Harga", + "prices_generic_title": "Harga", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Tambahkan harga", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Produk tidak ditemukan", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Pencarian produk", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "Lihat harga", + "prices_list_length_one_page": "{count,plural, =0{Belum ada harga} =1{Hanya satu harga} other{Semua {count} harga}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Harga {pageSize} terbaru (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Jumlah", + "prices_amount_is_discounted": "Apakah didiskon?", + "prices_amount_price_normal": "Harga", + "prices_amount_price_discounted": "Harga sesudah diskon", + "prices_amount_price_not_discounted": "Harga asli", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Nilai tidak valid", + "prices_amount_price_mandatory": "Nilai wajib", + "prices_currency_subtitle": "Mata uang", + "prices_date_subtitle": "Tanggal", + "prices_location_subtitle": "Toko", + "prices_location_find": "Temukan toko", + "prices_location_mandatory": "Anda harus memilih toko!", + "prices_proof_subtitle": "Bukti", + "prices_proof_find": "Pilih bukti", + "prices_proof_receipt": "Resi", + "prices_proof_price_tag": "Label harga", + "prices_proof_mandatory": "Anda harus memilih bukti!", + "prices_add_validation_error": "Kesalahan validasi", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Produk tidak diketahui", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Selesai", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Kecualikan Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Gunakan kembali dan sunting pencarian ini", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Harga saya", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Bukti saya", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Semua produk yang akan diselesaikan", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "Harga akan dikirim ke server sesegera mungkin.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Memulai penyegaran semua produk yang disimpan secara lokal", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Pilih mata uang Anda:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Pilih bahasa:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (Perhitungan baru)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (Perhitungan baru)", "nutriscore_unknown": "Nutri-Score tidak diketahui", + "nutriscore_unknown_new_formula": "Nutri-Score tidak diketahui (Perhitungan baru)", "nutriscore_not_applicable": "Nutri-Score tidak dapat diterapkan", + "nutriscore_not_applicable_new_formula": "Nutri-Score tidak bisa diterapkan (Perhitungan baru)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Instal Open Beauty Facts untuk membuat basis data kosmetik", "faq_title_install_pet": "Instal Open Pet Food Facts untuk membuat basis data makanan hewan peliharaan", "faq_title_install_product": "Instal Open Products Facts untuk membuat basis data produk guna memperpanjang masa pakai objek", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Impor produk Anda di Open Food Facts", "contact_title_pro_email": "Kontak Produser", "contact_title_press_page": "Halaman Pers", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Tautan ini tidak dapat dibuka di perangkat Anda. Pastikan Anda telah menginstal browser.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Rincian untuk {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Rincian untuk {pageName} dengan {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Panduan", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Bagikan", + "guide_nutriscore_v2_enabled": "benar", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "Nutri-Score terus berkembang: penjelasan!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Apa itu Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "Nutri-Score merupakan sebuah logo yang bertujuan untuk memberikan informasi kepada Anda tentang **kualitas nutrisi makanan**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "Kode warna bervariasi mulai dari hijau tua (**A**) untuk produk **tersehat** hingga merah tua (**E**) untuk produk *kurang sehat**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "Logo Nutri-Score A", + "guide_nutriscore_v2_why_v2_title": "Mengapa Nutri-Score terus berkembang?", + "guide_nutriscore_v2_why_v2_intro": "Formula Nutri-Score **berkembang** agar bisa memberikan rekomendasi yang lebih baik:", + "guide_nutriscore_v2_why_v2_arg1_title": "Lebih baik dalam mengevaluasi semua minuman", + "guide_nutriscore_v2_why_v2_arg1_text": "Catatan perbandingan antara **susu**, **minuman susu* dengan gula tambahan, dan minuman berbasis **sayuran** dibedakan secara lebih baik dengan algoritma baru.", + "guide_nutriscore_v2_why_v2_arg2_title": "Pemeringkatan minuman lebih baik", + "guide_nutriscore_v2_why_v2_arg2_text": "**Kandungan gula** diperhitungan dengan lebih baik dan mengutamakan minuman **rendah pemanis**. \\n**Pemanis juga akan dikenakan sanksi**: soda diet akan diturunkan peringkatnya dari B ke antara C dan E. Air tetap minuman yang direkomendasikan.", + "guide_nutriscore_v2_why_v2_arg3_title": "Garam dan gula dikenai sanksi", + "guide_nutriscore_v2_why_v2_arg3_text": "Produk yang **terlalu manis** atau **terlalu asin** akan makin mengalami *penurunan peringkat**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarki minyak dan ikan", + "guide_nutriscore_v2_why_v2_arg4_text": "Peringkat **ikan berlemak** tertentu dan **minyak kaya lemak baik** akan diperbaiki.", + "guide_nutriscore_v2_why_v2_arg5_title": "Pembatasan daging merah", + "guide_nutriscore_v2_why_v2_arg5_text": "Konsumsi **daging merah sebaiknya dibatasi**. Oleh karena itu, **unggas akan diberi peringkat yang relatif lebih baik**.", + "guide_nutriscore_v2_new_logo_title": "Bagaimana membedakan perhitungan Nutri-Score lama dan baru?", + "guide_nutriscore_v2_new_logo_text": "Mulai saat ini, logo dapat menampilkan penyebutan \"**Perhitungan baru**\" untuk memperjelas bahwa ini merupakan perhitungan baru.", + "guide_nutriscore_v2_new_logo_image_caption": "Logo baru Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "Nutri-Score diterapkan di beberapa negara: Jerman, Belgia, Spanyol, Prancis, Luksemburg, Belanda, dan Swiss.", + "guide_nutriscore_v2_where_paragraph2": "Produsen memiliki waktu paling lambat hingga **2026** **untuk mengganti** perhitungan lama dengan yang baru.", + "guide_nutriscore_v2_where_paragraph3": "Tanpa perlu menunggu, Anda **akan langsung menemukan di dalam aplikasi OpenFoodFacts**, perhitungan baru, termasuk jika produsen belum memperbarui skor.", + "guide_nutriscore_v2_unchanged_title": "Apa yang tidak berubah", + "guide_nutriscore_v2_unchanged_paragraph1": "Nutri-Score merupakan skor yang dirancang untuk **mengukur kualitas nutrisi**. Ini **melengkapi klasifikasi grup NOVA** untuk **makanan ultra proses** (yang juga ada di dalam aplikasi).", + "guide_nutriscore_v2_unchanged_paragraph2": "Bagi produsen, tampilan Nutri-Score **tetap opsional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ii.arb b/packages/smooth_app/lib/l10n/app_ii.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_ii.arb +++ b/packages/smooth_app/lib/l10n/app_ii.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_is.arb b/packages/smooth_app/lib/l10n/app_is.arb index 1683fc34477..9fe368ece44 100644 --- a/packages/smooth_app/lib/l10n/app_is.arb +++ b/packages/smooth_app/lib/l10n/app_is.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_it.arb b/packages/smooth_app/lib/l10n/app_it.arb index 6ea7a98ca48..2492bb195b3 100644 --- a/packages/smooth_app/lib/l10n/app_it.arb +++ b/packages/smooth_app/lib/l10n/app_it.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Un'illustrazione con Nutri-Score e Eco-Score sconosciuti", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Foto frontale del prodotto", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Conferma il caricamento della foto frontale del Prodotto", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Foto degli Ingredienti", "nutritional_facts_photo_title": "Foto valori nutrizionali", "recycling_photo_title": "Foto delle istruzioni di riciclaggio", + "take_photo_title": "Scatta una foto", "take_more_photo_title": "Scatta più foto", "front_photo_uploaded": "Foto frontale caricata", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Nessun prodotto trovato", "@no_product_found": {}, + "no_location_found": "Nessuna posizione individuata", "not_found": "non trovato:", - "searchPanelHeader": "Cerca o scansiona il tuo primo prodotto", - "@Product query status": {}, "refreshing_product": "Aggiornamento prodotto", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Immagine acquisita in data {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Immagine acquisita in data {date}. Questa immagine potrebbe essere obsoleta", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Benvenuti in Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scansiona** un codice a barre o\n**cerca** un prodotto", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Cerca un prodotto", + "homepage_main_card_search_field_tooltip": "Inizia la ricerca", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Ti piace l'app?", "tagline_app_review_button_positive": "La adoro! 😍", "tagline_app_review_button_negative": "Non proprio…", "tagline_app_review_button_later": "Chiedimelo più tardi", + "tagline_feed_news_button": "Scopri di più", "app_review_negative_modal_title": "Non ti piace la nostra app?", "app_review_negative_modal_text": "Potrebbe dedicarci qualche secondo per dirci perché?", "app_review_negative_modal_positive_button": "Sì, assolutamente!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "Si è verificato un errore!", + "product_internet_error_modal_message": "Non siamo in grado di recuperare informazioni su questo prodotto a causa di un errore di rete. Controlla la tua connessione internet e riprova.\n\nErrore interno:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossibile recuperare le informazioni su questo prodotto a causa di un errore di rete.", "cached_results_from": "Mostra i risultati da:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Scegli una valuta", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Hai appena cambiato Paese.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Vuoi cambiare la valuta da {previousCurrency} a {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Ti preghiamo di scegliere un paese:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -897,11 +974,11 @@ "count": {} } }, - "compare_products_mode": "Compara prodotti", + "compare_products_mode": "Compara i prodotti", "@compare_products_mode": { "description": "Button to switch to 'compare products mode'" }, - "compare_products_appbar_title": "Compara prodotti", + "compare_products_appbar_title": "Compara i prodotti", "@compare_products_appbar_title": { "description": "AppBar title when in comparison mode " }, @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Abbiamo inventato\nl'app di scansione collaborativa\nnel 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Mentre giriamo 10,\nlo stiamo reinventando\nda zero!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Benvenuto !", + "onboarding_home_welcome_text2": "L'app che ti aiuta a scegliere il cibo che fa bene a **te** e al **pianeta**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continua", "onboarding_welcome_loading_dialog_title": "Caricamento del tuo primo prodotto d'esempio", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibrazione e Tatto", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Completa i dettagli di base", "not_implemented_snackbar_text": "Non ancora implementato", "category_picker_page_appbar_text": "Categorie", - "edit_ingredients_extrait_ingredients_btn_text": "Estrai ingredienti", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Estrai gli ingredienti dalla foto", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Estrazione degli ingredienti\ndalla foto", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Caricamento della foto…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Aggiorna foto", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Estrarre la confezione", + "edit_packaging_extract_btn_text": "Estrai la confezione\ndalla foto", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Estrazione della confezione dalla foto", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Caricamento della foto…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Aggiorna foto", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Collegamento all'app Prezzi sulla pagina del prodotto", "prices_app_button": "Vai all'app Prezzi", + "prices_generic_title": "Prezzi", + "prices_add_n_prices": "{count,plural, one {}=1{Aggiungi un prezzo} other{Aggiungi {count} prezzi}}", + "prices_send_n_prices": "{count,plural, =1{Invia il prezzo} other{Invia {count} prezzi}}", + "prices_add_an_item": "Aggiungi un elemento", + "prices_add_a_price": "Aggiungi un prezzo", + "prices_add_a_receipt": "Aggiungi una ricevuta", + "prices_add_price_tags": "Aggiungi prezzi", + "prices_barcode_search_not_found": "Prodotto non trovato", + "prices_barcode_search_none_yet": "Ancora nessun prodotto", + "prices_barcode_search_question": "Vuoi cercare questo prodotto?", + "prices_barcode_search_title": "Ricerca prodotti", + "prices_barcode_search_running": "Ricerca del codice a barre {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Lettore del codice a barre", + "prices_view_prices": "Guarda i prezzi", + "prices_list_length_one_page": "{count,plural, =0{Ancora nessun prezzo} =1{Solo un prezzo} other{Tutti e {count} i prezzi}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Ultimi {pageSize} prezzi (totale: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{Ancora nessuna prova} =1{Solo una prova} other{Tutte e {count} le prove}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Ultime {pageSize} prove (totale: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Principali {pageSize} contributori (totali: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Quantità", + "prices_amount_is_discounted": "È scontato?", + "prices_amount_price_normal": "Prezzo", + "prices_amount_price_discounted": "Prezzo scontato", + "prices_amount_price_not_discounted": "Prezzo originale", + "prices_amount_no_product": "Manca un prodotto!", + "prices_amount_price_incorrect": "Valore non corretto", + "prices_amount_price_mandatory": "Valore obbligatorio", + "prices_currency_subtitle": "Moneta", + "prices_date_subtitle": "Data", + "prices_location_subtitle": "Negozio", + "prices_location_find": "Trova un negozio", + "prices_location_mandatory": "Devi selezionare un negozio!", + "prices_proof_subtitle": "Prova", + "prices_proof_find": "Seleziona una prova", + "prices_proof_receipt": "Ricevuta", + "prices_proof_price_tag": "Etichetta del prezzo", + "prices_proof_mandatory": "Devi selezionare una prova!", + "prices_add_validation_error": "Errore di validazione", + "prices_privacy_warning_title": "Avviso sulla privacy", + "prices_privacy_warning_message": "I prezzi saranno pubblici, insieme al negozio a cui si riferiscono.\nQuesto potrebbe permettere alle persone che conoscono il tuo pseudonimo di Open Food Facts di:\n* dedurre in quale zona vivi\n* sapere cosa stai comprando\nSe ti senti a disagio con ciò, cambia il tuo pseudonimo, oppure crea un nuovo account di Open Food Facts e accedi all'app con esso.", + "prices_unknown_product": "Prodotto sconosciuto", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Fatto", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Configurazione del server", + "dev_mode_section_product_page": "Pagina del prodotto", + "dev_mode_section_ui": "Interfaccia utente", + "dev_mode_section_data": "Dati", + "dev_mode_section_experimental_features": "Funzionalità sperimentali", "dev_mode_hide_ecoscore_title": "Escludi Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Riutilzza e modifica questa ricerca", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,11 +1891,43 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "I miei prezzi", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Le mie prove", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "La mia prova", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Prezzi dei contributori", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Ultimi prezzi aggiunti", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Collaboratori principali prezzi", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Negozi con più prezzi", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Prodotti con più prezzi", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Tutti i prodotti da completare", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" }, - "categorize_products_country_title": "Help categorize products in your country", + "categorize_products_country_title": "Aiuta a categorizzare i prodotti nel tuo paese", "@categorize_products_country_title": { "description": "Help categorize products in your country: list tile title" }, @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "Il prezzo verrà inviato al server il prima possibile.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Avviando il ricaricamento di tutti i prodotti memorizzati localmente", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Seleziona la tua valuta:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Seleziona la tua lingua:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (Nuovo calcolo)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (Nuovo calcolo)", "nutriscore_unknown": "Nutri-Score Sconosciuto", + "nutriscore_unknown_new_formula": "Nutri-Score sconosciuto (Nuovo calcolo)", "nutriscore_not_applicable": "Nutri-Score non applicabile", + "nutriscore_not_applicable_new_formula": "Nutri-Score non è applicabile (Nuovo calcolo)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Installa Open Beauty Facts per creare un database cosmetico", "faq_title_install_pet": "Installa Open Pet Food Facts per creare un database di alimenti per animali domestici", "faq_title_install_product": "Installa Open Products Facts per creare un database di prodotti per prolungare la vita degli oggetti", + "faq_nutriscore_nutriscore": "Nuovo calcolo del Nutri-Score: cosa c'è di nuovo?", "contact_title_pro_page": "Pro? Importa i tuoi prodotti in Open Food Facts", "contact_title_pro_email": "Contatto Produttore", "contact_title_press_page": "Pagina ufficio stampa", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Questo collegamento non può essere aperto sul tuo dispositivo. Verifica di avere un browser installato.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Dettagli per {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Dettagli per {pageName} con {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guida", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Condividi", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "Il Nutri-Score è in evoluzione: spiegazioni!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "it", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Cos'è il Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "Il Nutri-Score è un logo che ha lo scopo d'informarti sulla **qualità nutrizionale degli alimenti**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "Il codice colore varia dal verde scuro (**A**) per i prodotti **più salutari** al rosso scuro (**E**) per quelli **meno salutari**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "Il logo A del Nutri-Score", + "guide_nutriscore_v2_why_v2_title": "Perché il Nutri-Score si sta evolvendo?", + "guide_nutriscore_v2_why_v2_intro": "La formula del Nutri-Score **sta evolvendo** per fornire raccomandazioni migliori:", + "guide_nutriscore_v2_why_v2_arg1_title": "Valutazione migliore di tutte le bevande", + "guide_nutriscore_v2_why_v2_arg1_text": "Le note comparative di **latte**, **bevande lattiero-casearie** con zucchero aggiunto e **bevande vegetali** sono stati meglio differenziate nel nuovo algoritmo.", + "guide_nutriscore_v2_why_v2_arg2_title": "Migliore classificazione delle bevande", + "guide_nutriscore_v2_why_v2_arg2_text": "Il **contenuto di zucchero** viene meglio preso in considerazione e favorisce le bevande **poco zuccherate**.\\n**I dolcificanti verranno anch'essi penalizzati**: le bibite dietetiche passeranno da un punteggio B a uno compreso tra C ed E. L'acqua rimane la bevanda consigliata.", + "guide_nutriscore_v2_why_v2_arg3_title": "Il sale e lo zucchero penalizzati", + "guide_nutriscore_v2_why_v2_arg3_text": "I prodotti **troppo dolci** o **troppo salati** vedranno la loro **valutazione ulteriormente declassata**.", + "guide_nutriscore_v2_why_v2_arg4_title": "La gerarchia negli oli e nei pesci", + "guide_nutriscore_v2_why_v2_arg4_text": "La valutazione di alcuni **pesci grassi** e **oli ricchi in grassi buoni** migliorerà.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limita la carne rossa", + "guide_nutriscore_v2_why_v2_arg5_text": "Il consumo di **carne rossa dovrebbe essere limitato**. Ecco perché il **pollame sarà classificato relativamente meglio**.", + "guide_nutriscore_v2_new_logo_title": "Come distinguere tra il vecchio e il nuovo calcolo del Nutri-Score?", + "guide_nutriscore_v2_new_logo_text": "D'ora in poi, il logo può mostrare la dicitura \"**Nuovo calcolo**\" per chiarire che si tratta effettivamente del nuovo calcolo.", + "guide_nutriscore_v2_new_logo_image_caption": "Il logo del nuovo Nutri-Score", + "guide_nutriscore_v2_where_title": "Dove trovare il nuovo calcolo del Nutri-Score?", + "guide_nutriscore_v2_where_paragraph1": "Il Nutri-Score è in uso in diversi Paesi: Germania, Belgio, Spagna, Francia, Lussemburgo, Paesi Bassi e Svizzera.", + "guide_nutriscore_v2_where_paragraph2": "I produttori hanno tempo al massimo fino al **2026** **per sostituire** il vecchio calcolo con quello nuovo.", + "guide_nutriscore_v2_where_paragraph3": "Senza dover aspettare, **troverai già nell'applicazione OpenFoodFacts**, il nuovo calcolo, anche se i produttori non hanno aggiornato il punteggio.", + "guide_nutriscore_v2_unchanged_title": "Cosa non cambia", + "guide_nutriscore_v2_unchanged_paragraph1": "Il Nutri-Score è un punteggio progettato per **misurare la qualità nutrizionale**. È **complementare al gruppo NOVA** sugli **alimenti ultra-trasformati** (anch'esso presente nell'applicazione).", + "guide_nutriscore_v2_unchanged_paragraph2": "Per i produttori, l'esposizione del Nutri-Score **rimane facoltativa**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Anteprima", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_iu.arb b/packages/smooth_app/lib/l10n/app_iu.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_iu.arb +++ b/packages/smooth_app/lib/l10n/app_iu.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ja.arb b/packages/smooth_app/lib/l10n/app_ja.arb index 47eb2a285bc..1bfa02af466 100644 --- a/packages/smooth_app/lib/l10n/app_ja.arb +++ b/packages/smooth_app/lib/l10n/app_ja.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "製品の正面画像", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "製品の正面画像のアップロードを確認する", @@ -624,6 +628,7 @@ "ingredients_photo_title": "原材料の画像", "nutritional_facts_photo_title": "栄養成分表の画像", "recycling_photo_title": "リサイクル情報の画像", + "take_photo_title": "画像を撮る", "take_more_photo_title": "もっと写真を撮ります", "front_photo_uploaded": "正面の画像をアップロードしました", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "製品が見つかりません", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "見つかりません:", - "searchPanelHeader": "最初の製品を検索またはスキャン", - "@Product query status": {}, "refreshing_product": "製品を更新中", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "商品を検索", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "アプリを気に入っていただけましたか?", "tagline_app_review_button_positive": "大好きです! 😍", "tagline_app_review_button_negative": "そうでもないんですが...", "tagline_app_review_button_later": "あとで", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "このアプリを気に入っていますか?", "app_review_negative_modal_text": "その理由を少し教えていただけますか?", "app_review_negative_modal_positive_button": "そのとおり!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "ネットワークエラーのため、この製品に関する情報を取得できません。", "cached_results_from": "結果の最終更新:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "通貨を選んでください", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "国を変更しました。", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "通貨を {previousCurrency} から {possibleCurrency} に変更しますか?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "国を選択してください", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "2012年に\n協同スキャンアプリを\n開発しました", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "10周年に合わせて\n一からアプリを\n再開発しています!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "続ける", "onboarding_welcome_loading_dialog_title": "最初の見本となる製品を読み込んでいます", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "振動と触覚フィードバック", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "基本情報を完成させる", "not_implemented_snackbar_text": "まだ実装されていません", "category_picker_page_appbar_text": "カテゴリー", - "edit_ingredients_extrait_ingredients_btn_text": "原材料を抽出", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "画像を更新", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "パッケージ情報を抽出", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "画像を更新", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "製品ページの価格アプリへのショートカット", "prices_app_button": "価格アプリに移動", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "割引価格", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "領収書", + "prices_proof_price_tag": "値札", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "完了", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "サーバー設定", + "dev_mode_section_product_page": "製品ページ", + "dev_mode_section_ui": "ユーザインタフェース", + "dev_mode_section_data": "データ", + "dev_mode_section_experimental_features": "試験的な機能", "dev_mode_hide_ecoscore_title": "Eco-Scoreを無効にする", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "この検索を再利用して編集する", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "私の価格", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "自分の証拠", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "すべての未完成の製品", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "ローカルに保存されているすべての製品の更新を開始します", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "通貨を選択してください。", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "言語を選択してください:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "ニュートリスコア {letter} (新しい計算)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "ニュートリスコア(新しい計算)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "未知の栄養スコア(新しい計算)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "ニュートリスコアは適用されません(新しい計算)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Open Beauty Facts をインストールして化粧品データベースを作成する", "faq_title_install_pet": "Open Pet Food Facts をインストールしてペットフード データベースを作成する", "faq_title_install_product": "Open Products Facts をインストールして製品データベースを作成し、オブジェクトの寿命を延ばす", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "プロ? Open Food Facts に製品をインポートします", "contact_title_pro_email": "プロデューサー連絡先", "contact_title_press_page": "プレスページ", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "このリンクは、お使いのデバイスでは開けません。ブラウザがインストールされていることを確認してください。", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "共有", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "プレビュー", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_jv.arb b/packages/smooth_app/lib/l10n/app_jv.arb index 268deed37fb..6c4b65f2656 100644 --- a/packages/smooth_app/lib/l10n/app_jv.arb +++ b/packages/smooth_app/lib/l10n/app_jv.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ka.arb b/packages/smooth_app/lib/l10n/app_ka.arb index 973715bf98e..f1dc68c9413 100644 --- a/packages/smooth_app/lib/l10n/app_ka.arb +++ b/packages/smooth_app/lib/l10n/app_ka.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "მონაცემები", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_kk.arb b/packages/smooth_app/lib/l10n/app_kk.arb index 49adbb8707e..31602b9438c 100644 --- a/packages/smooth_app/lib/l10n/app_kk.arb +++ b/packages/smooth_app/lib/l10n/app_kk.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Суретке түсіру", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Санаттар", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Деректер", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Бөлісу", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_km.arb b/packages/smooth_app/lib/l10n/app_km.arb index 3f2c95108bc..781be4d126e 100644 --- a/packages/smooth_app/lib/l10n/app_km.arb +++ b/packages/smooth_app/lib/l10n/app_km.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "ចែករំលែក", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_kn.arb b/packages/smooth_app/lib/l10n/app_kn.arb index 5388053c9b7..e6acc91c335 100644 --- a/packages/smooth_app/lib/l10n/app_kn.arb +++ b/packages/smooth_app/lib/l10n/app_kn.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "ಇನ್ನಷ್ಟು ಚಿತ್ರಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಿ", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "ವರ್ಗಗಳು", - "edit_ingredients_extrait_ingredients_btn_text": "ಪದಾರ್ಥಗಳನ್ನು ಹೊರತೆಗೆಯಿರಿ", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "ಹಂಚಿಕೊಳ್ಳಿ", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ko.arb b/packages/smooth_app/lib/l10n/app_ko.arb index 57387c14f3d..c0f68d34417 100644 --- a/packages/smooth_app/lib/l10n/app_ko.arb +++ b/packages/smooth_app/lib/l10n/app_ko.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "상품 전면 사진", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "상품 전면 사진 업로드 확인", @@ -624,6 +628,7 @@ "ingredients_photo_title": "재료 사진", "nutritional_facts_photo_title": "영양 성분 사진", "recycling_photo_title": "재활용 사진", + "take_photo_title": "사진을 촬영", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "전면 사진 갱신됨", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "상품 없음", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "찾을 수 없음:", - "searchPanelHeader": "첫 상품을 검색하거나 스캔", - "@Product query status": {}, "refreshing_product": "상품 새로고치는 중...", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "이 앱이 마음에 드시나요?", "tagline_app_review_button_positive": "최고에요! 😍", "tagline_app_review_button_negative": "별로...", "tagline_app_review_button_later": "나중에 물어봐 주세요", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "저희 앱이 맘에 안드세요?", "app_review_negative_modal_text": "잠시 시간을 내어 이유를 말씀해 주실래요?", "app_review_negative_modal_positive_button": "예, 물론입니다!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "네트워크 오류로 인해 이 제품에 대한 정보를 가져올 수 없습니다.", "cached_results_from": "결과 보기:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "2012년에\n협업을 위한\n스캔 앱을 개발했습니다", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "10년이 되어감에 따라,\n스캔 앱을 처음부터\n다시 개발해나가고 있습니다!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "계속하기", "onboarding_welcome_loading_dialog_title": "첫 예시 상품 불러오는 중", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "진동 및 햅틱", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "기본 세부 정보 완성", "not_implemented_snackbar_text": "아직 구현되지 않았어요", "category_picker_page_appbar_text": "카테고리", - "edit_ingredients_extrait_ingredients_btn_text": "성분 추출", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "사진 새로고침", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "포장 사진에서 추출", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "사진 새로고침", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "완료", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "데이터", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "EcoScore 제외", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "모든 미완성 상품", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "친환경 점수", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "공유하기", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "영어", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ku.arb b/packages/smooth_app/lib/l10n/app_ku.arb index fa965d7d2b1..22cdd3a1ecb 100644 --- a/packages/smooth_app/lib/l10n/app_ku.arb +++ b/packages/smooth_app/lib/l10n/app_ku.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Bigire a resma", "take_more_photo_title": "Zev resma bışıkının", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Vebir", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Dane", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Re", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_kw.arb b/packages/smooth_app/lib/l10n/app_kw.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_kw.arb +++ b/packages/smooth_app/lib/l10n/app_kw.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ky.arb b/packages/smooth_app/lib/l10n/app_ky.arb index cf031173fe2..e25dd64a1fc 100644 --- a/packages/smooth_app/lib/l10n/app_ky.arb +++ b/packages/smooth_app/lib/l10n/app_ky.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Бөлүшүү", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_la.arb b/packages/smooth_app/lib/l10n/app_la.arb index b3a8b801738..46f1db800b4 100644 --- a/packages/smooth_app/lib/l10n/app_la.arb +++ b/packages/smooth_app/lib/l10n/app_la.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_lb.arb b/packages/smooth_app/lib/l10n/app_lb.arb index ef1d17a9e2a..ccb380f1be2 100644 --- a/packages/smooth_app/lib/l10n/app_lb.arb +++ b/packages/smooth_app/lib/l10n/app_lb.arb @@ -4,7 +4,7 @@ "@sep": { "description": "Separator just before a colon (':'). Probably only populated in French and empty in other languages." }, - "yes": "Yes", + "yes": "Jo", "@yes": {}, "add": "Bäifügen", "@add": {}, @@ -31,7 +31,7 @@ "go_back_to_top": "Go back to top", "save": "Save", "save_confirmation": "Are you sure you want to save?", - "skip": "Skip", + "skip": "Iwwersprangen", "cancel": "Cancel", "@cancel": {}, "ignore": "Ignore", @@ -40,7 +40,7 @@ }, "close": "Close", "@close": {}, - "no": "No", + "no": "Nee", "@no": {}, "stop": "Stop", "@stop": {}, @@ -443,7 +443,7 @@ "@myPersonalizedRanking": { "description": "When you press this button, all products (in list or category) are sorted according to your preferences." }, - "ranking_tab_all": "All", + "ranking_tab_all": "Alles", "ranking_subtitle_match_yes": "A great match for you", "ranking_subtitle_match_no": "Very poor match", "ranking_subtitle_match_maybe": "Unknown match", @@ -476,7 +476,7 @@ "@category": { "description": "From a product list, there's a category filter: this is its title" }, - "category_all": "All", + "category_all": "Alles", "@category_al": { "description": "Top meta-entry on a category filter" }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Eng Foto maachen", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,18 +750,69 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", - "app_review_negative_modal_negative_button": "No", + "app_review_negative_modal_negative_button": "Nee", "could_not_refresh": "Could not refresh product", "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1267,11 +1346,11 @@ "@edit_product_form_item_labels_subtitle": { "description": "Product edition - Labels - SubTitle" }, - "edit_product_form_item_labels_hint": "label", + "edit_product_form_item_labels_hint": "Etikett", "@edit_product_form_item_labels_hint": { "description": "Product edition - Labels - input textfield hint" }, - "edit_product_form_item_labels_type": "label", + "edit_product_form_item_labels_type": "Etikett", "@edit_product_form_item_labels_type": { "description": "Product edition - Labels - input textfield label" }, @@ -1307,7 +1386,7 @@ "@edit_product_form_item_origins_explainer_2": { "description": "Product edition - Origins - input explainer, part 2" }, - "edit_product_form_item_countries_title": "Country", + "edit_product_form_item_countries_title": "Land", "@edit_product_form_item_countries_title": { "description": "Product edition - Countries - Title" }, @@ -1385,7 +1464,7 @@ "@edit_product_form_item_nutrition_facts_subtitle": { "description": "Product edition - Nutrition facts - SubTitle" }, - "edit_product_form_save": "Edit", + "edit_product_form_save": "Änneren", "@edit_product_form_save": { "description": "Product edition - Nutrition facts - Save button" }, @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategorien", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Daten", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1736,7 +1977,7 @@ "@summary_card_button_add_basic_details": { "description": "Summary card - Button to add details about the product" }, - "edit_photo_button_label": "Edit", + "edit_photo_button_label": "Änneren", "@edit_photo_button_label": { "description": "Edit photo button label" }, @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_lo.arb b/packages/smooth_app/lib/l10n/app_lo.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_lo.arb +++ b/packages/smooth_app/lib/l10n/app_lo.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_lt.arb b/packages/smooth_app/lib/l10n/app_lt.arb index e251a1772de..bb1e3be0bcf 100644 --- a/packages/smooth_app/lib/l10n/app_lt.arb +++ b/packages/smooth_app/lib/l10n/app_lt.arb @@ -68,15 +68,15 @@ "@unknown": { "description": "Short label for product list view: the compatibility of that product with your preferences is unknown" }, - "match_very_good": "Labai geras atitikimas", + "match_very_good": "Puikus atitikmuo", "@match_very_good": { "description": "Label for product page regarding product compatibility with the user preferences: very good match" }, - "match_good": "Geras atitikimas", + "match_good": "Geras atitikmuo", "@match_good": { "description": "Label for product page regarding product compatibility with the user preferences: good match" }, - "match_poor": "Blogas atitikimas", + "match_poor": "Prastas atitikmuo", "@match_poor": { "description": "Label for product page regarding product compatibility with the user preferences: poor match" }, @@ -88,19 +88,19 @@ "@match_does_not": { "description": "Label for product page regarding product compatibility with the user preferences: does not match" }, - "match_unknown": "Nežinomas pasirinkimas", + "match_unknown": "Nežinomas atitikmuo", "@match_unknown": { "description": "Label for product page regarding product compatibility with the user preferences: unknown match" }, - "match_short_very_good": "Labai geras atitikimas", + "match_short_very_good": "Puikus atitikmuo", "@match_short_very_good": { "description": "Short label for product list view regarding product compatibility with the user preferences: very good match" }, - "match_short_good": "Geras atitikimas", + "match_short_good": "Geras atitikmuo", "@match_short_good": { "description": "Short label for product list view regarding product compatibility with the user preferences: good match" }, - "match_short_poor": "Blogas atitikimas", + "match_short_poor": "Prastas atitikmuo", "@match_short_poor": { "description": "Short label for product list view regarding product compatibility with the user preferences: poor match" }, @@ -112,7 +112,7 @@ "@match_short_does_not": { "description": "Short label for product list view regarding product compatibility with the user preferences: does not match" }, - "match_short_unknown": "Nežinomas pasirinkimas", + "match_short_unknown": "Nežinomas atitikmuo", "@match_short_unknown": { "description": "Short label for product list view regarding product compatibility with the user preferences: unknown match" }, @@ -125,7 +125,7 @@ "@Introduction screen": {}, "welcomeToOpenFoodFacts": "Sveiki atvykę į Open Food Facts", "@welcomeToOpenFoodFacts": {}, - "whatIsOff": "Open Food Facts is a global non-profit powered by local communities.", + "whatIsOff": "„Open Food Facts“ yra pasaulinė ne pelno siekianti organizacija, kurios veiklą skatina vietos bendruomenės.", "@whatIsOff": { "description": "Description of Open Food Facts organization." }, @@ -145,11 +145,11 @@ "@ecoCardUtility": { "description": "Description of what a user can use the Eco data in a product for." }, - "server_error_open_new_issue": "No server response! You may open an issue with the following link.", + "server_error_open_new_issue": "Serveris nepasiekimas! Galite iškelti problemą naudodami šią nuorodą.", "@user_management": {}, - "sign_in_text": "Sign in to your Open Food Facts account to save your contributions", + "sign_in_text": "Prisijunkite prie „Open Food Facts“ paskyros, kad jūsų įneštas savanoriškas indėlis būtų išsaugotas", "incorrect_credentials": "Neteisingas prisijungimo vardas arba slaptažodis.", - "password_lost_incorrect_credentials": "This email or username doesn't exist. Please check your credentials.", + "password_lost_incorrect_credentials": "Šis el. pašto adresas arba naudotojo vardas neegzistuoja. Patikrinkite savo prisijungimo duomenis.", "password_lost_server_unavailable": "We are currently experiencing slowdowns on our servers and we apologise for it. Please try again later.", "login": "Prisijungti", "@login": { @@ -297,7 +297,7 @@ "@darkmode_system_default": { "description": "Indicator inside the darkmode switch (system default)" }, - "thanks_for_contributing": "Thanks for contributing!", + "thanks_for_contributing": "Ačiū, kad prisidėjote!", "contributors_label": "Jie kuria programėlę", "@contributors_label": { "description": "Button label: Opens a pop up window where all contributors of this app are shown" @@ -332,7 +332,7 @@ "@legalNotices": { "description": "A link to open the legal notices on the website" }, - "privacy_policy": "Privacy policy", + "privacy_policy": "Privatumo politika", "@privacy_policy": { "description": "A link to open the privacy policy on the website" }, @@ -349,7 +349,7 @@ "@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": "Kiekvieno „Open Food Facts“ produkto kodą galite rasti „GitHub“. Kviečiame pakartotinai naudoti kodą (jis yra atvirojo kodo) ir padėti mums jį tobulinti visiems, visoje planetoje.", "@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": {}, @@ -429,7 +429,7 @@ "description": "Join which is actually Signup" }, "myPreferences_profile_title": "Jūsų profilis", - "myPreferences_profile_subtitle": "Manage your Open Food Facts contributor account.", + "myPreferences_profile_subtitle": "Tvarkykite savo „Open Food Facts“ paskyrą.", "myPreferences_settings_title": "Programos nustatymai", "myPreferences_settings_subtitle": "Tamsusis režimas; Analizė…", "myPreferences_food_title": "Maisto pasirinkimai", @@ -445,8 +445,8 @@ }, "ranking_tab_all": "Visi", "ranking_subtitle_match_yes": "Puikus pasirinkimas jums", - "ranking_subtitle_match_no": "Labai prastas pasirinkimas", - "ranking_subtitle_match_maybe": "Nežinomas pasirinkimas", + "ranking_subtitle_match_no": "Itin prastas atitikmuo", + "ranking_subtitle_match_maybe": "Nežinomas atitikmuo", "refresh_with_new_preferences": "Atnaujinkite sąrašą naudodami naujas nuostatas", "@refresh_with_new_preferences": { "description": "Action button label: Refresh the list with your new preferences" @@ -554,7 +554,7 @@ "@new_product_dialog_title": { "description": "Please keep it short, like 50 characters. Title of the dialog when the user searched for an unknown barcode." }, - "new_product_leave_title": "Leave this page?", + "new_product_leave_title": "Palikti šį puslapį?", "@new_product_leave_title": { "description": "Alert dialog title when a user landed on the 'add new product' page, didn't input anything and tried to leave the page." }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Produkto priekinė nuotrauka", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Patvirtinkite gaminio priekinės nuotraukos įkėlimą", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Sudėties Nuotrauka", "nutritional_facts_photo_title": "Maistingumo duomenų nuotrauka", "recycling_photo_title": "Perdirbimo Nuotrauka", + "take_photo_title": "Fotografuoti", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Priekinė nuotrauka įkelta", "@front_photo_uploaded": {}, @@ -674,7 +679,7 @@ "@take_more_photo_button_label": {}, "other_photo_uploaded": "Įkelta įvairi nuotrauka", "@other_photo_uploaded": {}, - "retake_photo_button_label": "Retake", + "retake_photo_button_label": "Pakartoti", "@retake_photo_button_label": { "description": "Button clicking on which allows users to retake the last photo they took." }, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Prekės nerasta", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "nerastas:", - "searchPanelHeader": "Ieškokite arba nuskaitykite pirmąjį produktą", - "@Product query status": {}, "refreshing_product": "Produktas atnaujinamas", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Nuotrauka padaryta {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Nuotrauka padaryta {date}. Ši nuotrauka gali būti pasenusi", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Sveiki atvykę į „Open Food Facts“", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Ieškoti produkto", + "homepage_main_card_search_field_tooltip": "Pradėti paiešką", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Ar jums patinka ši programėlė?", "tagline_app_review_button_positive": "Man ji labai patinka! 😍", "tagline_app_review_button_negative": "Nelabai…", "tagline_app_review_button_later": "Paklauskite manęs vėliau", + "tagline_feed_news_button": "Sužinoti daugiau", "app_review_negative_modal_title": "Jums nepatinka mūsų programėlė?", "app_review_negative_modal_text": "Ar galėtumėte skirti kelias sekundes ir pasakyti kodėl?", "app_review_negative_modal_positive_button": "Taip, žinoma!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "Įvyko klaida!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Neįmanoma gauti informacijos apie šį gaminį dėl tinklo klaidos.", "cached_results_from": "Rodyti rezultatus iš:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "2012 m. išradome\nbendradarbiavimo\nnuskaitymo programą", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Mums sukankant 10 metų,\nmes jį perkuriame\nnuo pat pradžių!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Tęsti", "onboarding_welcome_loading_dialog_title": "Įkeliamas jūsų pirmasis produkto pavyzdys", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibracija ir haptika", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Užpildyti pagrindinę informaciją", "not_implemented_snackbar_text": "Dar neįgyvendinta", "category_picker_page_appbar_text": "Kategorijos", - "edit_ingredients_extrait_ingredients_btn_text": "Nuskaityti ingredientus", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Atnaujinti nuotrauką", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Išgauti pakavimą", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Atnaujinti nuotrauką", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Kainos", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Pridėti kainą", + "prices_add_a_receipt": "Pridėti kvitą", + "prices_add_price_tags": "Pridėti kainų etiketes", + "prices_barcode_search_not_found": "Prekė nerasta", + "prices_barcode_search_none_yet": "Dar nėra produktų", + "prices_barcode_search_question": "Ar norite ieškoti šio produkto?", + "prices_barcode_search_title": "Produktų paieška", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Kaina", + "prices_amount_price_discounted": "Sumažinta kaina", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "Trūksta vieno produkto!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Valiuta", + "prices_date_subtitle": "Data", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Įrodymai", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Čekis", + "prices_proof_price_tag": "Kainos etiketė", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Kainos bus skelbiamos viešai, kartu pateikiant informaciją apie parduotuvę, kurioje produktas buvo pirktas.\nTai gali leisti žmonėms, žinantiems jūsų „Open Food Facts“ slapyvardį:\n* padaryti išvadą, kurioje vietovėje gyvenate\n* sužinoti, ką perkate\nJei tai jums nepatinka, galite pakeisti savo slapyvardį arba sukurti naują „Open Food Facts“ paskyrą ir su ja prisijungti prie programėlės.", + "prices_unknown_product": "Nežinomas produktas", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Atlikta", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Duomenys", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Neįtraukti Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Naudoti pakartotinai ir redaguoti šią paiešką", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1659,7 +1868,7 @@ } } }, - "product_search_loading_message": "Your search of {search} is in progress.\n\nPlease wait a few seconds…", + "product_search_loading_message": "Vykdoma jūsų paieška - {search}.\n\nŠiek tiek luktelėkite…", "@product_search_loading_message": { "description": "This message will be displayed when a search is in progress.", "search": { @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Mano kainos", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Mano įrodymai", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Visi baigtini gaminiai", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1902,7 +2143,7 @@ "@gallery_source_label": { "description": "Label for the gallery image source" }, - "gallery_source_access_denied_dialog_title": "Access denied", + "gallery_source_access_denied_dialog_title": "Prieiga uždrausta", "@gallery_source_access_denied_dialog_title": { "description": "On iOS, the user has refused to give the permission (title of the dialog)" }, @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Pasirinkite savo kalbą:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Įdiekite „Open Beauty Facts“, kad sukurtumėte kosmetikos duomenų bazę", "faq_title_install_pet": "Įdiekite „Open Pet Food Facts“, kad sukurtumėte naminių gyvūnų pašarų duomenų bazę", "faq_title_install_product": "Įdiekite „Open Products Facts“, kad sukurtumėte produktų duomenų bazę ir pailgintumėte objektų eksploatavimo laiką", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "„Pro“? Importuokite savo produktus į „Open Food Facts“", "contact_title_pro_email": "Gamintojo kontaktai", "contact_title_press_page": "Spaudos puslapis", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Šios nuorodos negalima atidaryti jūsų įrenginyje. Patikrinkite, ar įdiegta naršyklė.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Dalintis", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Kas yra „Nutri-Score“?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_lv.arb b/packages/smooth_app/lib/l10n/app_lv.arb index 44e16c622e0..4ad54b5bc97 100644 --- a/packages/smooth_app/lib/l10n/app_lv.arb +++ b/packages/smooth_app/lib/l10n/app_lv.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Fotografēt", "take_more_photo_title": "Uzņemt papildus fotogrāfijas", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategorijas", - "edit_ingredients_extrait_ingredients_btn_text": "Ekstrakta sastāvdaļas", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Dati", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Dalīties", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_mg.arb b/packages/smooth_app/lib/l10n/app_mg.arb index d57585a0ff8..1b02b9c351c 100644 --- a/packages/smooth_app/lib/l10n/app_mg.arb +++ b/packages/smooth_app/lib/l10n/app_mg.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Makà sary", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Sokajy", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Firaketana", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_mi.arb b/packages/smooth_app/lib/l10n/app_mi.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_mi.arb +++ b/packages/smooth_app/lib/l10n/app_mi.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ml.arb b/packages/smooth_app/lib/l10n/app_ml.arb index 88916e791d0..32ed0e37b08 100644 --- a/packages/smooth_app/lib/l10n/app_ml.arb +++ b/packages/smooth_app/lib/l10n/app_ml.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "വിഭാഗങ്ങൾ", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_mn.arb b/packages/smooth_app/lib/l10n/app_mn.arb index 0cb761bc854..8a115b77caa 100644 --- a/packages/smooth_app/lib/l10n/app_mn.arb +++ b/packages/smooth_app/lib/l10n/app_mn.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Ангилалууд", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_mr.arb b/packages/smooth_app/lib/l10n/app_mr.arb index afc5296e77b..271cbc1b4d9 100644 --- a/packages/smooth_app/lib/l10n/app_mr.arb +++ b/packages/smooth_app/lib/l10n/app_mr.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ms.arb b/packages/smooth_app/lib/l10n/app_ms.arb index d0bbe88d42b..1f0388e91eb 100644 --- a/packages/smooth_app/lib/l10n/app_ms.arb +++ b/packages/smooth_app/lib/l10n/app_ms.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Foto Ramuan", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Foto Kitar Semula", + "take_photo_title": "Ambil gambar", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Tiada Produk Ditemui", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "tidak ditemui:", - "searchPanelHeader": "Cari atau imbas produk pertama anda", - "@Product query status": {}, "refreshing_product": "Produk yang diperbaharui", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Lengkapkan butiran asas", "not_implemented_snackbar_text": "Belum dilaksanakan lagi", "category_picker_page_appbar_text": "Kategori-kategori", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Kongsi", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_mt.arb b/packages/smooth_app/lib/l10n/app_mt.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_mt.arb +++ b/packages/smooth_app/lib/l10n/app_mt.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_my.arb b/packages/smooth_app/lib/l10n/app_my.arb index 995b8cb6857..3f4d81b5caf 100644 --- a/packages/smooth_app/lib/l10n/app_my.arb +++ b/packages/smooth_app/lib/l10n/app_my.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "ေဝမွ်မည္", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_nb.arb b/packages/smooth_app/lib/l10n/app_nb.arb index 55a236f6615..5832be15d1a 100644 --- a/packages/smooth_app/lib/l10n/app_nb.arb +++ b/packages/smooth_app/lib/l10n/app_nb.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Bilde av ingredienser", "nutritional_facts_photo_title": "Bilde av ernæringsfakta", "recycling_photo_title": "Bilde av resirkuleringsinformasjon", + "take_photo_title": "Ta et bilde", "take_more_photo_title": "Ta flere bilder", "front_photo_uploaded": "Framsidebilde lastet opp", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "not found:", - "searchPanelHeader": "Søk etter eller skann ditt første produkt", - "@Product query status": {}, "refreshing_product": "Refreshing product", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Fullfør grunnleggende detaljer", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategorier", - "edit_ingredients_extrait_ingredients_btn_text": "Hent ut ingredienser", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Oppdater bildet", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Hent ut emballasje", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Oppdater bildet", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Fant ikke produktet", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Ferdig", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Alle produkter som skal fullføres", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starter oppdateringen av alle produktene som er lagret lokalt", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Del", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Hva er Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ne.arb b/packages/smooth_app/lib/l10n/app_ne.arb index ce2f13750c6..265c5e357ac 100644 --- a/packages/smooth_app/lib/l10n/app_ne.arb +++ b/packages/smooth_app/lib/l10n/app_ne.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_nl.arb b/packages/smooth_app/lib/l10n/app_nl.arb index 418a083208d..478c8712e5c 100644 --- a/packages/smooth_app/lib/l10n/app_nl.arb +++ b/packages/smooth_app/lib/l10n/app_nl.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Een illustratie met onbekende Nutri-Score en Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Foto van voorkant verpakking", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Bevestig het uploaden van de foto van de voorkant", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Foto van de ingrediëntenlijst", "nutritional_facts_photo_title": "Foto van voedingswaarden", "recycling_photo_title": "Recycling afbeelding", + "take_photo_title": "Maak een foto", "take_more_photo_title": "Maak meer foto's", "front_photo_uploaded": "Foto voorkant geüpload", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Geen product gevonden", "@no_product_found": {}, + "no_location_found": "Geen locatie gevonden", "not_found": "niet gevonden:", - "searchPanelHeader": "Zoek of scan uw eerste product", - "@Product query status": {}, "refreshing_product": "Product herladen", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Foto gemaakt op {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Afbeelding gemaakt op {date}. Deze afbeelding is mogelijk verouderd", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welkom bij Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** een streepjescode of\n**zoek** naar een product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Zoek naar een product", + "homepage_main_card_search_field_tooltip": "Begin met zoeken", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Laatste nieuws: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Vind je de app leuk?", "tagline_app_review_button_positive": "Ik vind het geweldig! 😍", "tagline_app_review_button_negative": "Niet echt…", "tagline_app_review_button_later": "Vraag me later nog eens", + "tagline_feed_news_button": "Meer weten", "app_review_negative_modal_title": "Vind je onze app niet leuk?", "app_review_negative_modal_text": "Kunt u een paar seconden de tijd nemen om ons te vertellen waarom?", "app_review_negative_modal_positive_button": "Ja, absoluut!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "Er is een fout opgetreden!", + "product_internet_error_modal_message": "We kunnen geen informatie over dit product ophalen vanwege een netwerkfout. Controleer uw internetverbinding en probeer het opnieuw.\n\nInterne fout:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Onmogelijk om informatie over dit product op te halen vanwege een netwerkfout.", "cached_results_from": "Resultaten weergeven van:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Kies een valuta", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Je bent zojuist van land veranderd.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Wilt u de valuta wijzigen van {previousCurrency} naar {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Kies een land:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We hebben in 2012\nde collaboratieve\nscan-app uitgevonden", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Naar aanleiding van ons 10-jarig bestaan, vinden we onszelf opnieuw uit!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welkom !", + "onboarding_home_welcome_text2": "De app die je helpt bij het kiezen van voedsel dat goed is voor **jou** en de **planeet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Doorgaan", "onboarding_welcome_loading_dialog_title": "Uw eerste voorbeeldproduct wordt geladen", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan een streepjescode met uw camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Trillen & haptieken", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Basisdetails aanvullen", "not_implemented_snackbar_text": "Nog niet geïmplementeerd", "category_picker_page_appbar_text": "Categorieën", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingrediënten", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Detecteer de ingrediënten op de foto", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Detecteer de ingrediënten\nop de foto", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Foto laden…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Foto vernieuwen", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Verpakking uitpakken", + "edit_packaging_extract_btn_text": "Detecteer de verpakking\nop de foto", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Detecteer de verpakking op de foto", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Foto laden…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Foto vernieuwen", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Snelkoppeling naar de Prijzen-app op de productpagina", "prices_app_button": "Ga naar de Prijzen-app", + "prices_generic_title": "Prijzen", + "prices_add_n_prices": "{count,plural, one {}=1{Voeg 1 prijs toe} other{Voeg {count} prijzen toe}}", + "prices_send_n_prices": "{count,plural, one {}=1{Verstuur 1 prijs} other{Verstuur {count} prijzen}}", + "prices_add_an_item": "Item toevoegen", + "prices_add_a_price": "Voeg een prijs toe", + "prices_add_a_receipt": "Een ontvangstbewijs toevoegen", + "prices_add_price_tags": "Voeg prijskaartjes toe", + "prices_barcode_search_not_found": "Geen product gevonden", + "prices_barcode_search_none_yet": "Nog geen product", + "prices_barcode_search_question": "Wilt u dit product zoeken?", + "prices_barcode_search_title": "Product zoeken", + "prices_barcode_search_running": "Zoeken naar {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Streepjescodelezer", + "prices_view_prices": "Bekijk de prijzen", + "prices_list_length_one_page": "{count,plural, one {}=0{Nog geen prijs} =1{Slechts één prijs} other{Alle {count} prijzen}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Laatste {pageSize} prijzen (totaal: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, one {}=0{Nog geen bewijs} =1{Slechts één bewijs} other{Alle {count} bewijzen}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Laatste {pageSize} bewijzen (totaal: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} bijdragers (totaal: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Hoeveelheid", + "prices_amount_is_discounted": "Wordt er korting gegeven?", + "prices_amount_price_normal": "Prijs", + "prices_amount_price_discounted": "Gereduceerde prijs", + "prices_amount_price_not_discounted": "Oorspronkelijke prijs", + "prices_amount_no_product": "Er ontbreekt één product!", + "prices_amount_price_incorrect": "Onjuiste waarde", + "prices_amount_price_mandatory": "Verplichte waarde", + "prices_currency_subtitle": "Valuta", + "prices_date_subtitle": "Datum", + "prices_location_subtitle": "Winkel", + "prices_location_find": "Zoek een winkel", + "prices_location_mandatory": "Je moet een winkel selecteren!", + "prices_proof_subtitle": "Bewijs", + "prices_proof_find": "Selecteer een bewijs", + "prices_proof_receipt": "Bon", + "prices_proof_price_tag": "Prijskaartje", + "prices_proof_mandatory": "Je moet een bewijs selecteren!", + "prices_add_validation_error": "Validatiefout", + "prices_privacy_warning_title": "Privacywaarschuwing", + "prices_privacy_warning_message": "De prijzen zijn openbaar, samen met de winkel waarnaar ze verwijzen.\nHierdoor kunnen mensen die uw Open Food Facts-pseudoniem kennen het volgende doen:\n* afleiden in welk gebied u woont\n* weten wat u koopt\n$Als u zich daar niet prettig bij voelt, wijzig dan uw pseudoniem of maak een nieuw Open Food Facts-account aan en log daarmee in op de app.", + "prices_unknown_product": "Onbekend product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Voltooid", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Serverconfiguratie", + "dev_mode_section_product_page": "Productpagina", + "dev_mode_section_ui": "Gebruikersinterface", + "dev_mode_section_data": "Gegevens", + "dev_mode_section_experimental_features": "Experimentele functies", "dev_mode_hide_ecoscore_title": "Eco-Score uitsluiten", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Gebruik een spellingcontrole voor OCR-schermen", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingrediënten en verpakking)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Hergebruik en bewerk deze zoekopdracht", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Mijn prijzen", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Mijn bewijzen", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "Mijn bewijs", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Prijzen van bijdragers", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Laatst toegevoegde prijzen", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Beste prijsbijdragers", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Winkels met de meeste prijzen", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Producten met de meeste prijzen", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Alle te voltooien producten", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "De prijs wordt zo snel mogelijk naar de server verzonden.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Het vernieuwen starten van alle producten die lokaal zijn opgeslagen", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Selecteer uw valuta:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Selecteer uw taal:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (Nieuwe berekening)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (Nieuwe berekening)", "nutriscore_unknown": "Onbekende Nutri-Score", + "nutriscore_unknown_new_formula": "Onbekende Nutri-Score (Nieuwe berekening)", "nutriscore_not_applicable": "Nutri-Score is niet van toepassing", + "nutriscore_not_applicable_new_formula": "Nutri-Score is niet van toepassing (Nieuwe berekening)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Installeer Open Beauty Facts om een cosmetische database te maken", "faq_title_install_pet": "Installeer Open Pet Food Facts om een dierenvoedingsdatabase te maken", "faq_title_install_product": "Installeer Open Products Facts om een productendatabase te creëren om de levensduur van objecten te verlengen", + "faq_nutriscore_nutriscore": "Nieuwe berekening van de Nutri-Score: wat is er nieuw?", "contact_title_pro_page": "Pro? Importeer je producten in Open Food Facts", "contact_title_pro_email": "Producentcontact", "contact_title_press_page": "Perspagina", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Deze link kan niet worden geopend op uw apparaat. Controleer of u een browser heeft geïnstalleerd.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details voor {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details voor {pageName} met {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Gids", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Delen", + "guide_nutriscore_v2_enabled": "waar", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "De Nutri-Score Evolueert: Toelichtingen!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "nl", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Wat is de Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "De Nutri-Score is een logo dat tot doel heeft u te informeren over de **voedingskwaliteit van voedingsmiddelen**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "De kleurcode varieert van donkergroen (**A**) voor de **gezondste** producten tot donkerrood (**E**) voor de **minder gezonde** producten.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "Het Nutri-Score A-logo", + "guide_nutriscore_v2_why_v2_title": "Waarom evolueert de Nutri-Score?", + "guide_nutriscore_v2_why_v2_intro": "De Nutri-Score-formule **evolueert** om betere aanbevelingen te doen:", + "guide_nutriscore_v2_why_v2_arg1_title": "Evalueert alle drankjes beter", + "guide_nutriscore_v2_why_v2_arg1_text": "De relatieve aantekeningen van **melk**, **zuiveldranken** met toegevoegde suikers en **groentedranken** werden beter gedifferentieerd in het nieuwe algoritme.", + "guide_nutriscore_v2_why_v2_arg2_title": "Betere rangschikking van drankjes", + "guide_nutriscore_v2_why_v2_arg2_text": "Er wordt meer rekening gehouden met het suikergehalte**, wat in voordeel is voor dranken met een laag suikergehalte.\\n** Zoetstoffen worden ook bestraft**: dieetdrankjes worden gedegradeerd van een B naar een C tot een E-score. Water is nog steeds de aanbevolen drank.", + "guide_nutriscore_v2_why_v2_arg3_title": "Zout en suiker bestraft", + "guide_nutriscore_v2_why_v2_arg3_text": "Producten die **te zoet** of **te zout** zijn, krijgen een lagere beoordeling**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hiërarchie binnen oliën en vis", + "guide_nutriscore_v2_why_v2_arg4_text": "De beoordeling van bepaalde **vette vis** en **oliën rijk aan goede vetten** zal verbeteren.", + "guide_nutriscore_v2_why_v2_arg5_title": "Beperk rood vlees", + "guide_nutriscore_v2_why_v2_arg5_text": "De consumptie van **rood vlees moet beperkt worden**. Daarom zal **gevogelte relatief beter scoren**.", + "guide_nutriscore_v2_new_logo_title": "Hoe onderscheid ik de oude Nutri-Score van de nieuwe berekening?", + "guide_nutriscore_v2_new_logo_text": "Vanaf nu kan het logo een vermelding \"**Nieuwe berekening**\" weergeven om te verduidelijken dat het daadwerkelijk om de nieuwe berekening gaat.", + "guide_nutriscore_v2_new_logo_image_caption": "Het logo van de nieuwe Nutri-Score", + "guide_nutriscore_v2_where_title": "Waar vind je de nieuwe Nutri-Score berekening?", + "guide_nutriscore_v2_where_paragraph1": "De Nutri-Score wordt in meerdere landen toegepast: Duitsland, België, Spanje, Frankrijk, Luxemburg, Nederland en Zwitserland.", + "guide_nutriscore_v2_where_paragraph2": "Fabrikanten hebben tot uiterlijk **2026** de tijd om de oude berekening door de nieuwe te **vervangen**.", + "guide_nutriscore_v2_where_paragraph3": "Zonder te moeten wachten **vind je in de OpenFoodFacts applicatie** al de nieuwe berekening, ook als de fabrikanten de score nog niet hebben aangepast.", + "guide_nutriscore_v2_unchanged_title": "Wat niet verandert", + "guide_nutriscore_v2_unchanged_paragraph1": "De Nutri-Score is een score die is ontworpen om **de voedingskwaliteit te meten**. Het is **complementair aan de NOVA-groep** op **ultrabewerkte voedingsmiddelen** (ook aanwezig in de applicatie).", + "guide_nutriscore_v2_unchanged_paragraph2": "Voor fabrikanten blijft de weergave van de Nutri-Score **optioneel**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Voorbeeld", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_nn.arb b/packages/smooth_app/lib/l10n/app_nn.arb index a1e540b31c6..1731558508f 100644 --- a/packages/smooth_app/lib/l10n/app_nn.arb +++ b/packages/smooth_app/lib/l10n/app_nn.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Ta et bilde", "take_more_photo_title": "Ta flere bilder", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategorier", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Del", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_no.arb b/packages/smooth_app/lib/l10n/app_no.arb index a1e540b31c6..1731558508f 100644 --- a/packages/smooth_app/lib/l10n/app_no.arb +++ b/packages/smooth_app/lib/l10n/app_no.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Ta et bilde", "take_more_photo_title": "Ta flere bilder", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategorier", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Del", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_nr.arb b/packages/smooth_app/lib/l10n/app_nr.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_nr.arb +++ b/packages/smooth_app/lib/l10n/app_nr.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_oc.arb b/packages/smooth_app/lib/l10n/app_oc.arb index 5120c5b1fa3..efd39816bd6 100644 --- a/packages/smooth_app/lib/l10n/app_oc.arb +++ b/packages/smooth_app/lib/l10n/app_oc.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categorias", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Donadas", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Partejar", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_or.arb b/packages/smooth_app/lib/l10n/app_or.arb index 54db538378c..b10968f9050 100644 --- a/packages/smooth_app/lib/l10n/app_or.arb +++ b/packages/smooth_app/lib/l10n/app_or.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_pa.arb b/packages/smooth_app/lib/l10n/app_pa.arb index 1a1b43c1eb4..a3502859bad 100644 --- a/packages/smooth_app/lib/l10n/app_pa.arb +++ b/packages/smooth_app/lib/l10n/app_pa.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_pl.arb b/packages/smooth_app/lib/l10n/app_pl.arb index f3495579a32..47b3825a9ed 100644 --- a/packages/smooth_app/lib/l10n/app_pl.arb +++ b/packages/smooth_app/lib/l10n/app_pl.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Zdjęcie produktu z przodu", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Potwierdź przesłanie zdjęcia produktu", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Zdjęcie listy składników", "nutritional_facts_photo_title": "Zdjęcie tabeli wartości odżywczych", "recycling_photo_title": "Zdjęcie informacji o recyklingu", + "take_photo_title": "Zrób zdjęcie", "take_more_photo_title": "Zrób więcej zdjęć", "front_photo_uploaded": "Przesłano zdjęcie przodu", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Nie znaleziono produktu", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "nie znaleziono:", - "searchPanelHeader": "Wyszukaj lub zeskanuj swój pierwszy produkt", - "@Product query status": {}, "refreshing_product": "Odświeżanie produktu", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Wyszukaj produkt", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Nie można pobrać informacji o tym produkcie z powodu błędu sieciowego.", "cached_results_from": "Pokaż wyniki z:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "W 2012 roku\nstworzyliśmy aplikację\ndo wspólnego skanowania", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Po 10. latach działania\npostanowiliśmy zaprojektować\naplikację od zera!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Kontynuuj", "onboarding_welcome_loading_dialog_title": "Ładowanie pierwszego przykładowego produktu", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Wibracje i Haptyczna Informacja Zwrotna", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Uzupełnij podstawowe informacje", "not_implemented_snackbar_text": "Jeszcze niezaimplementowane", "category_picker_page_appbar_text": "Kategorie", - "edit_ingredients_extrait_ingredients_btn_text": "Wyodrębnij składniki", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Odśwież zdjęcie", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Rozpoznaj opakowanie", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Odśwież zdjęcie", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Ceny", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Dodaj cenę", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Produkt nie został znaleziony", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Cena", + "prices_amount_price_discounted": "Cena promocyjna", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Waluta", + "prices_date_subtitle": "Data", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Dowód", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Rachunek", + "prices_proof_price_tag": "Cenówka", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Nieznany produkt", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Gotowe", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Dane", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Wyklucz EcoScore", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Użyj ponownie i edytuj to wyszukiwanie", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Moje ceny", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Moje dowody", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Wszystkie produkty do uzupełnienia", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Rozpoczynanie odświeżania wszystkich lokalnie przechowywanych produktów", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Nie można otworzyć tego odnośnika na Twoim urządzeniu. Sprawdź, czy masz zainstalowaną przeglądarkę.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Udostępnij", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Czym jest Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_pt.arb b/packages/smooth_app/lib/l10n/app_pt.arb index d4b41660315..14dccb75a57 100644 --- a/packages/smooth_app/lib/l10n/app_pt.arb +++ b/packages/smooth_app/lib/l10n/app_pt.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Uma ilustração com Nutri-Score e Eco-Score desconhecidos", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Fotografia frontal da embalagem", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirmar envio da fotografia frontal da embalagem", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Fotografia dos ingredientes", "nutritional_facts_photo_title": "Fotografia das informações nutricionais", "recycling_photo_title": "Fotografia de reciclagem", + "take_photo_title": "Tirar uma foto", "take_more_photo_title": "Tirar mais fotografias", "front_photo_uploaded": "Fotografia inicial enviada", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Não foi encontrado nenhum produto", "@no_product_found": {}, + "no_location_found": "Nenhuma localização encontrada", "not_found": "não encontrado:", - "searchPanelHeader": "Pesquise ou digitalize o seu primeiro produto", - "@Product query status": {}, "refreshing_product": "A atualizar o produto", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Procurar por um produto", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Gosta da aplicação?", "tagline_app_review_button_positive": "Adoro! 😍", "tagline_app_review_button_negative": "Nem por isso…", "tagline_app_review_button_later": "Perguntar depois", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Não gosta da nossa aplicação?", "app_review_negative_modal_text": "Pode tirar uns segundos e dizer-nos o porquê por favor?", "app_review_negative_modal_positive_button": "Sim, absolutamente!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Não foi possível obter informações sobre este produto devido a um erro de rede.", "cached_results_from": "Mostrar resultados de:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Por favor selecione uma moeda", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Acabou de mudar de país.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Quer alterar a moeda de {previousCurrency} para {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Escolha um país:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Inventamos\na aplicação de digitalização\ncolaborativa em 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Ao fazermos 10 anos,\nestamos a reinventá-la\na partir do zero!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continuar", "onboarding_welcome_loading_dialog_title": "A carregar o seu primeiro exemplo de produto", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibração e resposta tátil", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Detalhes básicos completos", "not_implemented_snackbar_text": "Ainda não implementado", "category_picker_page_appbar_text": "Categorias", - "edit_ingredients_extrait_ingredients_btn_text": "Extrair ingredientes", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Atualizar fotografia", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extrair embalagem", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Atualizar fotografia", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Atalho para a aplicação Preços na página do produto", "prices_app_button": "Ir para a aplicação Preços", + "prices_generic_title": "Preços", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Adicionar um preço", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Produto não encontrado", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Pesquisa de produtos", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "Ver os preços", + "prices_list_length_one_page": "{count,plural, =0{Ainda sem preço} =1{Apenas um preço} other{Todos os {count} preços}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Últimos {pageSize} preços (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Montante", + "prices_amount_is_discounted": "Está com desconto?", + "prices_amount_price_normal": "Preço", + "prices_amount_price_discounted": "Preço descontado", + "prices_amount_price_not_discounted": "Preço original", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Valor incorreto", + "prices_amount_price_mandatory": "Valor obrigatório", + "prices_currency_subtitle": "Moeda", + "prices_date_subtitle": "Data", + "prices_location_subtitle": "Loja", + "prices_location_find": "Encontrar uma loja", + "prices_location_mandatory": "Tem de selecionar uma loja!", + "prices_proof_subtitle": "Demonstração", + "prices_proof_find": "Selecionar um comprovativo", + "prices_proof_receipt": "Recibo", + "prices_proof_price_tag": "Etiqueta de preço", + "prices_proof_mandatory": "Tem de selecionar um comprovativo!", + "prices_add_validation_error": "Erro de validação", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Produto desconhecido", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Concluído", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Dados", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Excluir Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reproduzir e editar esta pesquisa", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Meus valores", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Minhas demonstrações", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Todos os produtos a serem concluídos", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "O preço será enviado para o servidor o mais rapidamente possível.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "A iniciar a atualização de todos os produtos armazenados localmente", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Selecione a sua moeda:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Selecione o seu idioma:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (novo cálculo)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (novo cálculo)", "nutriscore_unknown": "Nutri-Score desconhecido", + "nutriscore_unknown_new_formula": "Nutri-Score desconhecido (novo cálculo)", "nutriscore_not_applicable": "Nutri-Score não aplicável", + "nutriscore_not_applicable_new_formula": "O Nutri-Score não é aplicável (novo cálculo)", "ecoscore_generic": "Eco-Pontuação", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Instale o Open Beauty Facts para criar uma base de dados de cosméticos", "faq_title_install_pet": "Instale o Open Pet Food Facts para criar uma base de dados de comida para animais de estimação", "faq_title_install_product": "Instale o Open Products Facts para criar uma base de dados de produtos para prolongar a vida útil dos objetos", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Profissional? Importe os seus produtos no Open Food Facts", "contact_title_pro_email": "Contacto do produtor", "contact_title_press_page": "Página de imprensa", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Este link não pode ser aberto no seu dispositivo. Por favor, verifique se você tem um navegador instalado.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Detalhes de {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Detalhes de {pageName} com {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guia", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Partilhar", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "pt", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "O que não muda", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_qu.arb b/packages/smooth_app/lib/l10n/app_qu.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_qu.arb +++ b/packages/smooth_app/lib/l10n/app_qu.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_rm.arb b/packages/smooth_app/lib/l10n/app_rm.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_rm.arb +++ b/packages/smooth_app/lib/l10n/app_rm.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ro.arb b/packages/smooth_app/lib/l10n/app_ro.arb index 26f8b0c837d..c911d649da4 100644 --- a/packages/smooth_app/lib/l10n/app_ro.arb +++ b/packages/smooth_app/lib/l10n/app_ro.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Fotografie din față a produsului", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirmați încărcarea fotografiei ambalajului frontal", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Fotografie cu ingredientele", "nutritional_facts_photo_title": "Informații nutriționale Foto", "recycling_photo_title": "Imaginea cu metoda de reciclare", + "take_photo_title": "Faceți o fotografie", "take_more_photo_title": "Faceți mai multe poze", "front_photo_uploaded": "Fotografia frontală a fost încărcată", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Nu a fost găsit niciun produs", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "nu a fost găsit:", - "searchPanelHeader": "Cauta sau scaneaza primul produs", - "@Product query status": {}, "refreshing_product": "Reîmprospătare produs", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Îți place aplicația?", "tagline_app_review_button_positive": "O ador! 😍", "tagline_app_review_button_negative": "Nu chiar...", "tagline_app_review_button_later": "Întreabă-mă mai târziu", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Nu îți place aplicația noastră?", "app_review_negative_modal_text": "Poți să aloci câteva secunde pentru a ne spune de ce?", "app_review_negative_modal_positive_button": "Da, cu siguranță!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Imposibil de preluat informații despre acest produs din cauza unei erori de rețea.", "cached_results_from": "Afișați rezultate de la:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Vă rugăm să alegeți o țară:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Am inventat\naplicația de scanare colaborativă\nîn 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Când împlinim 10 ani,\no reinventăm\nde la zero!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continuați", "onboarding_welcome_loading_dialog_title": "Se încarcă primul exemplu de produs", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibrație și haptică", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Detalii de bază complete", "not_implemented_snackbar_text": "Neimplementat încă", "category_picker_page_appbar_text": "Categorii", - "edit_ingredients_extrait_ingredients_btn_text": "Extrageți ingredientele", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Reîmprospătați fotografia", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extragere ambalaj", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Reîmprospătați fotografia", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Produsul nu a fost găsit", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Terminat", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Date", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Excludeți Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reutilizați și editați această căutare", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Toate produsele de completat", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Începe reîmprospătarea tuturor produselor stocate local", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Selectați limba dvs.:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Scor nutrițional C", "nutriscore_d": "Scor nutrițional D", "nutriscore_e": "Scor nutrițional E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Scor nutrițional necunoscut", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Scorul nutrițional nu se aplică", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Scor Ecologic", "ecoscore_a": "Scor Ecologic A", "ecoscore_b": "Scor Ecologic B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Instalați Open Beauty Facts pentru a crea o bază de date cu produse cosmetice", "faq_title_install_pet": "Instalați Open Pet Food Facts pentru a crea o bază de date cu alimente pentru animalele de companie", "faq_title_install_product": "Instalați Open Products Facts pentru a crea o bază de date de produse pentru a prelungi durata de viață a obiectelor", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Importați-vă produsele în Open Food Facts", "contact_title_pro_email": "Contact producător", "contact_title_press_page": "Apăsați Pagina", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Acest link nu poate fi deschis pe dispozitivul dvs. Vă rugăm să verificați dacă aveți un browser instalat.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Distribuiți", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ru.arb b/packages/smooth_app/lib/l10n/app_ru.arb index 1afd77477e5..71a326333cc 100644 --- a/packages/smooth_app/lib/l10n/app_ru.arb +++ b/packages/smooth_app/lib/l10n/app_ru.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Фотография лицевой стороны продукта", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Подтвердить загрузку фотографии лицевой стороны продукта", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Фото состава", "nutritional_facts_photo_title": "Фото информации о пищевой ценности", "recycling_photo_title": "Изображение сведений о переработке", + "take_photo_title": "Сфотографировать", "take_more_photo_title": "Сделайте дополнительные фото", "front_photo_uploaded": "Загружено переднее фото", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Продукт не найден", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "не найдено:", - "searchPanelHeader": "Найдите или просканируйте свой первый продукт", - "@Product query status": {}, "refreshing_product": "Обновление продукта", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Нравится приложение?", "tagline_app_review_button_positive": "Конечно! 😍", "tagline_app_review_button_negative": "Не очень…", "tagline_app_review_button_later": "Позже", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Не нравится наше приложение?", "app_review_negative_modal_text": "Пожалуйста, напишите, почему.", "app_review_negative_modal_positive_button": "Да, конечно!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Невозможно получить информацию об этом продукте из-за сетевой ошибки.", "cached_results_from": "Показать результаты из:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "В 2012 году\nмы создали приложение\nдля коллективного сканирования", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Теперь когда нам 10 лет,\nнами было пересоздано\nполностью с нуля!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Продолжить", "onboarding_welcome_loading_dialog_title": "Загрузка вашего первого примера продукта", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Вибрация и отклик", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Детальная информация", "not_implemented_snackbar_text": "Пока что не реализовано", "category_picker_page_appbar_text": "Категории", - "edit_ingredients_extrait_ingredients_btn_text": "Распознать ингредиенты", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Обновить фото", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Извлечь информацию об упаковке", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Обновить фото", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Продукт не найден", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Готово", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Данные", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Исключить Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Все незавершённые продукты", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Вы производитель? Импортируйте данные о своих продуктах в Open Food Facts", "contact_title_pro_email": "Связаться с нами (для производителей)", "contact_title_press_page": "Материалы для прессы", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Поделиться", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "Англ.", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sa.arb b/packages/smooth_app/lib/l10n/app_sa.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_sa.arb +++ b/packages/smooth_app/lib/l10n/app_sa.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sc.arb b/packages/smooth_app/lib/l10n/app_sc.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_sc.arb +++ b/packages/smooth_app/lib/l10n/app_sc.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sd.arb b/packages/smooth_app/lib/l10n/app_sd.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_sd.arb +++ b/packages/smooth_app/lib/l10n/app_sd.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sg.arb b/packages/smooth_app/lib/l10n/app_sg.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_sg.arb +++ b/packages/smooth_app/lib/l10n/app_sg.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_si.arb b/packages/smooth_app/lib/l10n/app_si.arb index fbe9916b845..e211d286d36 100644 --- a/packages/smooth_app/lib/l10n/app_si.arb +++ b/packages/smooth_app/lib/l10n/app_si.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sk.arb b/packages/smooth_app/lib/l10n/app_sk.arb index 1c35ffb4fd1..68e27d8b0d5 100644 --- a/packages/smooth_app/lib/l10n/app_sk.arb +++ b/packages/smooth_app/lib/l10n/app_sk.arb @@ -302,7 +302,7 @@ "@contributors_label": { "description": "Button label: Opens a pop up window where all contributors of this app are shown" }, - "contributors_dialog_title": "Contributors", + "contributors_dialog_title": "Prispievatelia", "@contributors_dialog_title": { "description": "Dialog title: A list of all contributors of this app" }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Predná fotografia produktu", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Odfotiť", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Neboli nájdené žiadne výrobky", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "nenájdené:", - "searchPanelHeader": "Vyhľadajte alebo naskenujte svoj prvý produkt", - "@Product query status": {}, "refreshing_product": "Obnovuje sa produkt", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Vyhľadajte produkt", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Páči sa vám aplikácia?", "tagline_app_review_button_positive": "Milujem to! 😍", "tagline_app_review_button_negative": "Naozaj nie…", "tagline_app_review_button_later": "Opýtajte sa ma neskôr", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Nepáči sa vám naša aplikácia?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Áno, jednoznačne!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Nie je možné získať informácie o tomto produkte kvôli chybe siete.", "cached_results_from": "Zobraziť výsledky z:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Vyberte krajinu:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Pokračovať", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibrácie a haptika", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Kategórie", - "edit_ingredients_extrait_ingredients_btn_text": "Rozpoznať zložky", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Odkaz na aplikáciu Ceny na stránke produktu", "prices_app_button": "Prejdite do aplikácie Ceny", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Pridajte cenu", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Výrobok sa nenašiel", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Cena", + "prices_amount_price_discounted": "Zvýhodnená cena", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Mena", + "prices_date_subtitle": "Dátum", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Účtenka", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Hotovo", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Údaje", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Vylúčiť ekologické skóre", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Opätovne použite a upravte toto vyhľadávanie", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Moje ceny", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Všetky produkty, ktoré sa majú dokončiť", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Vyberte váš jazyk", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Neznáme Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score nie je použiteľné", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eko-skóre", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Nainštalujte si Open Beauty Facts a vytvorte kozmetickú databázu", "faq_title_install_pet": "Nainštalujte si Open Pet Food Facts a vytvorte databázu krmiva pre domáce zvieratá", "faq_title_install_product": "Nainštalujte si fakty o otvorených produktoch na vytvorenie databázy produktov na predĺženie životnosti objektov", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Importujte svoje produkty do Open Food Facts", "contact_title_pro_email": "Kontakt na výrobcu", "contact_title_press_page": "Stránka pre média", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Tento odkaz nie je možné otvoriť na vašom zariadení. Skontrolujte, či máte nainštalovaný prehliadač.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sl.arb b/packages/smooth_app/lib/l10n/app_sl.arb index 48344bfeac0..9ee10bb3ca8 100644 --- a/packages/smooth_app/lib/l10n/app_sl.arb +++ b/packages/smooth_app/lib/l10n/app_sl.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Fotografija sprednjega dela izdelka", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Potrdite naloženo sprednjo fotografijo izdelka", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Slika sestavin", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Fotografija recikliranja", + "take_photo_title": "Posnemi fotografijo", "take_more_photo_title": "Naredite več slik", "front_photo_uploaded": "Sprednja fotografija je naložena", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Ni zadetkov", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "ni zadetkov:", - "searchPanelHeader": "Poiščite ali skenirajte svoj prvi izdelek", - "@Product query status": {}, "refreshing_product": "Osvežitev izdelkov", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Zaradi napake v omrežju ni mogoče pridobiti informacij o tem izdelku.", "cached_results_from": "Prikaži rezultate iz:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Kooperativno aplikacijo\nza skeniranje smo\nizumili leta 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Ker smo dopolnili 10\nbomo preoblikovali vse na novo\nod glave do pete!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Nalaganje prvega primera izdelka", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Izpolnite osnovne podatke", "not_implemented_snackbar_text": "Še ni implementirano", "category_picker_page_appbar_text": "Kategorije", - "edit_ingredients_extrait_ingredients_btn_text": "Preberi sestavine", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Osveži fotografijo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Osveži fotografijo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Opravljeno", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Podatki", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Deli", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sn.arb b/packages/smooth_app/lib/l10n/app_sn.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_sn.arb +++ b/packages/smooth_app/lib/l10n/app_sn.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_so.arb b/packages/smooth_app/lib/l10n/app_so.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_so.arb +++ b/packages/smooth_app/lib/l10n/app_so.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sq.arb b/packages/smooth_app/lib/l10n/app_sq.arb index 7ef495a705c..26aa2799529 100644 --- a/packages/smooth_app/lib/l10n/app_sq.arb +++ b/packages/smooth_app/lib/l10n/app_sq.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Fotoja e perparme, ballore u ngarkua.", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Nuk u gjet asnje produkt", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "Nuk u gjet, nuk rezultoi ", - "searchPanelHeader": "Kerkoni ose skanoni produktin tuaj te pare", - "@Product query status": {}, "refreshing_product": "Produkt freskues", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Eshte e pamundur te merret informacion ne lidhje me kete produkt per shkak te nje gabimi ne rrjet.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sr.arb b/packages/smooth_app/lib/l10n/app_sr.arb index 205f8abd8d9..bd2e6dd1d0f 100644 --- a/packages/smooth_app/lib/l10n/app_sr.arb +++ b/packages/smooth_app/lib/l10n/app_sr.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Категорије", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Подаци", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Podeli", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ss.arb b/packages/smooth_app/lib/l10n/app_ss.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_ss.arb +++ b/packages/smooth_app/lib/l10n/app_ss.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_st.arb b/packages/smooth_app/lib/l10n/app_st.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_st.arb +++ b/packages/smooth_app/lib/l10n/app_st.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sv.arb b/packages/smooth_app/lib/l10n/app_sv.arb index 0acf43b4a92..6389bf35997 100644 --- a/packages/smooth_app/lib/l10n/app_sv.arb +++ b/packages/smooth_app/lib/l10n/app_sv.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Foto på Ingredienser", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Bild på återvinningsinformation", + "take_photo_title": "Ta en bild", "take_more_photo_title": "Ta fler bilder", "front_photo_uploaded": "Framsidefoto uppladdat", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Ingen produkt hittades", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "hittades inte:", - "searchPanelHeader": "Sök eller skanna din första produkt", - "@Product query status": {}, "refreshing_product": "Uppdaterar produkt", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Gillar du appen?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Inte riktigt…", "tagline_app_review_button_later": "Fråga mig senare", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Gillar du inte vår app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Ja, självklart!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Omöjligt att hämta information om denna produkt på grund av ett nätverksfel.", "cached_results_from": "Visa resultat från:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Laddar din första exempelprodukt", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Komplettera grundläggande uppgifter", "not_implemented_snackbar_text": "Inte implementerat ännu", "category_picker_page_appbar_text": "Kategorier", - "edit_ingredients_extrait_ingredients_btn_text": "Extrahera ingredienser", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Finner ej produkt", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Klar", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Välj ditt språk:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Dela", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_sw.arb b/packages/smooth_app/lib/l10n/app_sw.arb index 1ba9cfc30f8..fab8450a8da 100644 --- a/packages/smooth_app/lib/l10n/app_sw.arb +++ b/packages/smooth_app/lib/l10n/app_sw.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ta.arb b/packages/smooth_app/lib/l10n/app_ta.arb index 32dd8ce0ea4..a3800c17a56 100644 --- a/packages/smooth_app/lib/l10n/app_ta.arb +++ b/packages/smooth_app/lib/l10n/app_ta.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "அறியப்படாத நியூட்ரி-ஸ்கோர் மற்றும் எக்கோ-ஸ்கோர் கொண்ட ஒரு விளக்கம்", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "ஒரு படம் எடு", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "படம் எடுக்கப்பட்ட{date} அன்று", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "நாணயத்தைத் தேர்வு செய்யவும்", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "நீங்கள் இப்போதுதான் நாடுகளை மாற்றி இருக்கிறீர்கள்.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "உங்களின் முந்தைய நாணயத்திலிருந்து {previousCurrency} சாத்தியமான நாணயத்திற்கு {possibleCurrency} மாற்ற விரும்புகிறீர்களா?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "{barcode} ஐத் தேடுகிறது அல்லது தேடுகிறோம்", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "சிறந்த {pageSize} பங்களிப்பாளர்கள் (மொத்தம்: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "தரவு", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "எனது விலைகள்", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "பங்களிப்பாளர் விலைகள்", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "சமீபத்திய விலைகள் சேர்க்கப்பட்டன", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "அதிக விலை பங்களிப்பாளர்கள்", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "அதிக விலை கொண்ட கடைகள்", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "அதிக விலை கொண்ட தயாரிப்புகள்", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "உள்நாட்டில் சேமிக்கப்பட்ட அனைத்து தயாரிப்புகளையும் புதுப்பிக்கத் தொடங்குகிறது", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "உங்கள் நாணயத்தைத் தேர்ந்தெடுக்கவும்:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "நியூட்ரி-ஸ்கோர் {letter} (புதிய கணக்கீடு)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "தெரியாத நியூட்ரி ஸ்கோர் (புதிய கணக்கீடு)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "நியூட்ரி-ஸ்கோர் பொருந்தாது (புதிய கணக்கீடு)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "கையேடு", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "பகிர்க", + "guide_nutriscore_v2_enabled": "உண்மை", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_te.arb b/packages/smooth_app/lib/l10n/app_te.arb index 796bfb3869b..0c920c56da9 100644 --- a/packages/smooth_app/lib/l10n/app_te.arb +++ b/packages/smooth_app/lib/l10n/app_te.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "వర్గాలు", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_tg.arb b/packages/smooth_app/lib/l10n/app_tg.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_tg.arb +++ b/packages/smooth_app/lib/l10n/app_tg.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_th.arb b/packages/smooth_app/lib/l10n/app_th.arb index 2792e5eb08f..6ab989ddbe1 100644 --- a/packages/smooth_app/lib/l10n/app_th.arb +++ b/packages/smooth_app/lib/l10n/app_th.arb @@ -443,7 +443,7 @@ "@myPersonalizedRanking": { "description": "When you press this button, all products (in list or category) are sorted according to your preferences." }, - "ranking_tab_all": "All", + "ranking_tab_all": "ทั้งหมด", "ranking_subtitle_match_yes": "จับคู่ที่ดีสำหรับคุณ", "ranking_subtitle_match_no": "Very poor match", "ranking_subtitle_match_maybe": "Unknown match", @@ -476,7 +476,7 @@ "@category": { "description": "From a product list, there's a category filter: this is its title" }, - "category_all": "All", + "category_all": "ทั้งหมด", "@category_al": { "description": "Top meta-entry on a category filter" }, @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "รูปภาพส่วนประกอบของอาหาร", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "การนำกลับไปใช้ใหม่", + "take_photo_title": "ถ่ายภาพ", "take_more_photo_title": "ถ่ายรูปเพิ่มเติม", "front_photo_uploaded": "รูปภาพด้านหน้าอัพโหลดเรียบร้อย", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "ไม่พบผลิตภัณฑ์", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "ไม่พบ", - "searchPanelHeader": "ค้นหาหรือสแกนผลิตภัณฑ์แรกของคุณ", - "@Product query status": {}, "refreshing_product": "กำลังโหลดผลิตภัณฑ์ใหม่", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "ไม่สามารถดึงข้อมูลเกี่ยวกับผลิตภัณฑ์นี้ได้เนื่องจากข้อผิดพลาดของเครือข่าย", "cached_results_from": "แสดงผลลัพธ์จาก:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "หมวดหมู่", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "ข้อมูล", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "อธิบายเพิ่มเติมเกี่ยวกับ Nutri-Score", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ti.arb b/packages/smooth_app/lib/l10n/app_ti.arb index db59725dec9..d448c94a7d0 100644 --- a/packages/smooth_app/lib/l10n/app_ti.arb +++ b/packages/smooth_app/lib/l10n/app_ti.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_tl.arb b/packages/smooth_app/lib/l10n/app_tl.arb index 17a11e0d8c3..dbcf11b0994 100644 --- a/packages/smooth_app/lib/l10n/app_tl.arb +++ b/packages/smooth_app/lib/l10n/app_tl.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Kumuha ng larawan", "take_more_photo_title": "Kumuha ng mas maraming larawan", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Mga kategorya", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Datos", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Ibahagi", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_tn.arb b/packages/smooth_app/lib/l10n/app_tn.arb index 276b5888a6a..1e534e3b6c4 100644 --- a/packages/smooth_app/lib/l10n/app_tn.arb +++ b/packages/smooth_app/lib/l10n/app_tn.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_tr.arb b/packages/smooth_app/lib/l10n/app_tr.arb index 5ca0aa82e68..1ad5d380ce6 100644 --- a/packages/smooth_app/lib/l10n/app_tr.arb +++ b/packages/smooth_app/lib/l10n/app_tr.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Ürünün önden bir fotoğrafı", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Ürünün ön fotoğrafının yüklenmesini onaylayın", @@ -624,6 +628,7 @@ "ingredients_photo_title": "İçindekiler fotoğarafı", "nutritional_facts_photo_title": "Besin Değerleri Fotoğrafı", "recycling_photo_title": "Geri Dönüşüm Fotoğrafı", + "take_photo_title": "Resim çek", "take_more_photo_title": "Daha fazla fotoğraf çekin", "front_photo_uploaded": "Önden fotoğraf yüklendi", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Ürün bulunamadı", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "bulunamadı:", - "searchPanelHeader": "İlk ürününüzü arayın veya tarayın", - "@Product query status": {}, "refreshing_product": "Ürün yenileme", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Bir ürün arayın", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Uygulamayı beğendiniz mi?", "tagline_app_review_button_positive": "Bayıldım! 😍", "tagline_app_review_button_negative": "Pek sayılmaz…", "tagline_app_review_button_later": "Daha sonra sor", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "Uygulamamızı beğenmediniz mi?", "app_review_negative_modal_text": "Bize nedenini söylemek için birkaç saniyenizi ayırabilir misiniz?", "app_review_negative_modal_positive_button": "Evet kesinlikle!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Bir ağ hatasından dolayı ürün bilgileri çekilemedi.", "cached_results_from": "Sonuçların gösterildiği:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Lütfen bir para birimi seçin", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Az önce ülke değiştirdiniz.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Para birimini {previousCurrency} yerine {possibleCurrency} olarak değiştirmek istiyor musunuz?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Lütfen bir ülke seçin:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "İşbirliğine dayalı tarama uygulamasını 2012'de icat ettik", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "10 yaşına geldiğimizde, onu sıfırdan yeniden keşfediyoruz!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Devam et", "onboarding_welcome_loading_dialog_title": "İlk örnek ürününüz yükleniyor", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Titreşim ve Dokunma", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Temel bilgileri tamamlayın", "not_implemented_snackbar_text": "Henüz uygulanmadı", "category_picker_page_appbar_text": "Kategoriler", - "edit_ingredients_extrait_ingredients_btn_text": "İçindekileri çıkartın", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Fotoğrafı yenileyin", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Paketi ayıkla", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Fotoğrafı yenileyin", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Ürün sayfasındaki Fiyatlar uygulamasının kısayolu", "prices_app_button": "Fiyatlar uygulamasına gidin", + "prices_generic_title": "Fiyatlar", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Fiyat ekleyin", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Ürün bulunamadı", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Ürün Arama", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Fiyat", + "prices_amount_price_discounted": "İndirimli fiyat", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Kur", + "prices_date_subtitle": "Tarih", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Kanıt", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Makbuz", + "prices_proof_price_tag": "Fiyat etiketi", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Bilinmeyen ürün", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Yapıldı", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Veri", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "EcoScore'u hariç tut", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Bu aramayı yeniden kullan ve düzenle", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Fiyatlarım", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Bedellerim", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Tamamlanacak tüm ürünler", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Yerel olarak depolanan tüm ürünlerin yenilenmesinin başlatılması", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Para biriminizi seçin:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Dilinizi seçiniz:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (Yeni hesaplama)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Bilinmeyen Nutri-Score", + "nutriscore_unknown_new_formula": "Bilinmeyen Nutri-Score (Yeni hesaplama)", "nutriscore_not_applicable": "Nutri-Score geçerli değildir", + "nutriscore_not_applicable_new_formula": "Nutri-Score geçerli değildir (Yeni hesaplama)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Bir kozmetik veritabanı oluşturmak için Open Beauty Facts'i yükleyin", "faq_title_install_pet": "Bir evcil hayvan maması veri tabanı oluşturmak için Open Pet Food Facts'i kurun", "faq_title_install_product": "Nesnelerin ömrünü uzatmak amacıyla bir ürün veritabanı oluşturmak için Open Products Facts'i kurun", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Profesyonel mi? Open Food Facts'te ürünlerinizi içe aktarın", "contact_title_pro_email": "Üretici İletişim", "contact_title_press_page": "Basın Sayfası", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Bu bağlantı cihazınızda açılamıyor. Lütfen tarayıcınızın kurulu olup olmadığını kontrol edin.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Paylaş", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Nutri-Score nedir?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ts.arb b/packages/smooth_app/lib/l10n/app_ts.arb index 276b5888a6a..1e534e3b6c4 100644 --- a/packages/smooth_app/lib/l10n/app_ts.arb +++ b/packages/smooth_app/lib/l10n/app_ts.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_tt.arb b/packages/smooth_app/lib/l10n/app_tt.arb index 470bc8cd832..84cbf7f9024 100644 --- a/packages/smooth_app/lib/l10n/app_tt.arb +++ b/packages/smooth_app/lib/l10n/app_tt.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Әзер", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Мәгълүмат", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Бүлеш", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_tw.arb b/packages/smooth_app/lib/l10n/app_tw.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_tw.arb +++ b/packages/smooth_app/lib/l10n/app_tw.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ty.arb b/packages/smooth_app/lib/l10n/app_ty.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_ty.arb +++ b/packages/smooth_app/lib/l10n/app_ty.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ug.arb b/packages/smooth_app/lib/l10n/app_ug.arb index bbfa0ba1cab..facddd04004 100644 --- a/packages/smooth_app/lib/l10n/app_ug.arb +++ b/packages/smooth_app/lib/l10n/app_ug.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "رەسىمگە تارتىش", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "سانلىق مەلۇمات", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "ھەمبەھىرلەش", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_uk.arb b/packages/smooth_app/lib/l10n/app_uk.arb index 60a3a0a23db..bdc0ab8d9a3 100644 --- a/packages/smooth_app/lib/l10n/app_uk.arb +++ b/packages/smooth_app/lib/l10n/app_uk.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "Ілюстрація з невідомими Nutri-Score та Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Фото передньої частини продукту", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Підтвердьте завантаження передньої фотографії продукту", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Фото інгредієнтів", "nutritional_facts_photo_title": "Світлина харчової цінності продукту", "recycling_photo_title": "Обробка фото", + "take_photo_title": "Сфотографувати", "take_more_photo_title": "Зробити більше фото", "front_photo_uploaded": "Попереднє фото завантажено", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Продукт не знайдено", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "не знайдено:", - "searchPanelHeader": "Шукайте або відскануйте ваш перший товар", - "@Product query status": {}, "refreshing_product": "Освіжаючий товар", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Зображення зроблене {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Зображення, зроблене {date}. Це зображення може бути застарілим", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Вітаємо в Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Відскануйте** штрих-код або\n**знайдіть** продукт", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Пошук товару", + "homepage_main_card_search_field_tooltip": "Почати пошук", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Останні новини: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Вам подобається цей додаток?", "tagline_app_review_button_positive": "Я в захопленні! 😍", "tagline_app_review_button_negative": "Не дуже…", "tagline_app_review_button_later": "Запитайте мене пізніше", + "tagline_feed_news_button": "Докладніше", "app_review_negative_modal_title": "Вам не подобається наш додаток?", "app_review_negative_modal_text": "Не могли б ви витратити кілька секунд, щоб сказати нам, чому?", "app_review_negative_modal_positive_button": "Так, звичайно!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "Сталася помилка!", + "product_internet_error_modal_message": "Ми не можемо отримати інформацію про цей продукт через помилку мережі. Перевірте підключення до Інтернету та повторіть спробу.\n\nВнутрішня помилка:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Неможливо отримати інформацію про цей продукт через помилку мережі.", "cached_results_from": "Показати результати з:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Будь ласка виберіть валюту", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "Ви щойно змінили країну.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Ви хочете змінити валюту з {previousCurrency} на {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Будь ласка, виберіть країну:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Ми розробили додаток\nдля спільного сканування\nу 2012 році", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Оскільки нам виповнюється 10 років,\nми переосмислюємо його\nз нуля!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Ласкаво просимо!", + "onboarding_home_welcome_text2": "Додаток, який допоможе вам вибрати їжу, яка корисна для **вас** і **планети**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Продовжити", "onboarding_welcome_loading_dialog_title": "Завантаження вашого першого прикладу продукту", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Відскануйте штрих-код камерою", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Вібрація та тактильні відчуття", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Заповніть основні деталі", "not_implemented_snackbar_text": "Ще не реалізовано", "category_picker_page_appbar_text": "Категорії", - "edit_ingredients_extrait_ingredients_btn_text": "Вилучити інгредієнти", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Видобути інгредієнти з фото", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Отримання інгредієнтів із зображення", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Завантаження фото…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Оновити фото", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Розпакування пакунків", + "edit_packaging_extract_btn_text": "Видобути упаковку\nз фото", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Вилучення пакунка з фотографії", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Завантаження фото…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Оновити фото", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Ярлик програми «Ціни» на сторінці продукту", "prices_app_button": "Перейдіть у додаток Ціни", + "prices_generic_title": "Ціни", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Додати елемент", + "prices_add_a_price": "Додати ціну", + "prices_add_a_receipt": "Додати чек", + "prices_add_price_tags": "Додайте цінові бірки", + "prices_barcode_search_not_found": "Товар не знайдено", + "prices_barcode_search_none_yet": "Ще немає товару", + "prices_barcode_search_question": "Ви хочете шукати цей продукт?", + "prices_barcode_search_title": "Пошук товару", + "prices_barcode_search_running": "Шукаю {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Сканер штрих-коду", + "prices_view_prices": "Переглянути ціни", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Останні {pageSize} докази (всього: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Топ {pageSize} учасників (всього: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Кількість", + "prices_amount_is_discounted": "Чи є знижка?", + "prices_amount_price_normal": "Ціна", + "prices_amount_price_discounted": "Ціна зі знижкою", + "prices_amount_price_not_discounted": "Початкова ціна", + "prices_amount_no_product": "Бракує одного товару!", + "prices_amount_price_incorrect": "Помилкове значення", + "prices_amount_price_mandatory": "Обов'язкове значення", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Крамниця", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Доказ", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Квитанція", + "prices_proof_price_tag": "Цінник", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Попередження про конфіденційність", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Невідомий продукт", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Виконано", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Конфігурація сервера", + "dev_mode_section_product_page": "Сторінка продукту", + "dev_mode_section_ui": "Інтерфейс користувача", + "dev_mode_section_data": "Дані", + "dev_mode_section_experimental_features": "Експериментальні функції", "dev_mode_hide_ecoscore_title": "Вимкнути Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Використовуйте перевірку орфографії для OCR екранів", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Інгредієнти та упаковка)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Повторно використовуйте та редагуйте цей пошук", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "Мої ціни", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "Мої докази", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "Мої докази", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Ціни вкладників", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Останні додані ціни", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Найбільші учасники цін", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Магазини з найбільшими цінами", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Товари з найбільшою ціною", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Мої завершені товари", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Запуск оновлення всіх продуктів, які зберігаються локальна", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Оберіть Вашу валюту:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Виберіть свою мову:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Оцінка Nutri-Score C", "nutriscore_d": "Оцінка Nutri-Score D", "nutriscore_e": "Оцінка Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (новий розрахунок)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (Новий розрахунок)", "nutriscore_unknown": "Невідома оцінка Nutri-Score", + "nutriscore_unknown_new_formula": "Невідомий Nutri-Score (новий розрахунок)", "nutriscore_not_applicable": "Nutri-Score не застосовується", + "nutriscore_not_applicable_new_formula": "Nutri-Score не застосовується (новий розрахунок)", "ecoscore_generic": "Eco-Score (показник впливу на довкілля)", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Встановіть Open Beauty Facts для створення косметичної бази даних", "faq_title_install_pet": "Встановіть Open Pet Food Facts щоб створити базу даних для домашніх тварин", "faq_title_install_product": "Встановіть Open Products Facts, щоб створити базу даних продуктів і подовжити термін служби об’єктів", + "faq_nutriscore_nutriscore": "Новий розрахунок Nutri-Score: що нового?", "contact_title_pro_page": "Pro? Імпортуйте свої продукти в Open Food Facts", "contact_title_pro_email": "Контакт виробника", "contact_title_press_page": "Інформація для преси", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "Це посилання не може бути відкрито на вашому пристрої. Будь ласка, перевірте чи встановлено браузер.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Деталі для {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Деталі для {pageName} з {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Посібник", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Поділитися", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "Nutri-Score розвивається: пояснення!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "Що таке Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "Nutri-Score – це логотип, мета якого інформувати вас про **поживну якість харчових продуктів**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "Кольоровий код варіюється від темно-зеленого (**A**) для **найздоровіших** продуктів до темно-червоного (**E**) для **менш корисних** продуктів.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "Логотип Nutri-Score A", + "guide_nutriscore_v2_why_v2_title": "Чому Nutri-Score розвивається?", + "guide_nutriscore_v2_why_v2_intro": "Формула Nutri-Score **еволюціонує**, щоб надавати кращі рекомендації:", + "guide_nutriscore_v2_why_v2_arg1_title": "Краще оцініть всі напої", + "guide_nutriscore_v2_why_v2_arg1_text": "Порівняльні ноти **молока**, **молочних напоїв** з додаванням цукру та **овочевих** напоїв у новому алгоритмі краще розрізнили.", + "guide_nutriscore_v2_why_v2_arg2_title": "Кращий рейтинг напоїв", + "guide_nutriscore_v2_why_v2_arg2_text": "**Вміст цукру** краще брати до уваги, і він надає перевагу напоям із **слабким вмістом цукру**.\\n**Підсолоджувачі також будуть штрафуватися**: рейтинг дієтичних газованих напоїв буде знижено з B до C і E. Вода залишається рекомендованим напоєм.", + "guide_nutriscore_v2_why_v2_arg3_title": "Сіль і цукор штрафні", + "guide_nutriscore_v2_why_v2_arg3_text": "Продукти **занадто солодкі** або **занадто солоні** отримають **рейтинг ще більше знижений**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Ієрархія в оліях і рибі", + "guide_nutriscore_v2_why_v2_arg4_text": "Поліпшиться рейтинг певної **жирної риби** та **олій, багатих хорошими жирами**.", + "guide_nutriscore_v2_why_v2_arg5_title": "Обмежити червоне м'ясо", + "guide_nutriscore_v2_why_v2_arg5_text": "Споживання **червоного м’яса слід обмежити**. Ось чому **птах матиме порівняно кращий рейтинг**.", + "guide_nutriscore_v2_new_logo_title": "Як відрізнити старий Nutri-Score від нового розрахунку?", + "guide_nutriscore_v2_new_logo_text": "Відтепер логотип може показувати згадку «**Новий розрахунок**», щоб уточнити, що це справді новий розрахунок.", + "guide_nutriscore_v2_new_logo_image_caption": "Логотип нового Nutri-Score", + "guide_nutriscore_v2_where_title": "Де знайти новий розрахунок Nutri-Score?", + "guide_nutriscore_v2_where_paragraph1": "Nutri-Score застосовується в кількох країнах: Німеччині, Бельгії, Іспанії, Франції, Люксембурзі, Нідерландах та Швейцарії.", + "guide_nutriscore_v2_where_paragraph2": "Постачальники мають до **2026 року** останній термін **на заміну** старого розрахунку на новий.", + "guide_nutriscore_v2_where_paragraph3": "Без очікування ви вже знайдете в додатку OpenFoodFacts новий розрахунок, включаючи в тому числі якщо постачальники не оновили бали.", + "guide_nutriscore_v2_unchanged_title": "Що не змінилося", + "guide_nutriscore_v2_unchanged_paragraph1": "Nutri-Score – це оцінка, розроблена для **вимірювання якості харчування**. Він **доповнює групу NOVA** щодо **ультраоброблених продуктів** (також присутня в додатку).", + "guide_nutriscore_v2_unchanged_paragraph2": "Для постачальників зображення Nutri-Score **залишається необов’язковим**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Попередній перегляд", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ur.arb b/packages/smooth_app/lib/l10n/app_ur.arb index 6697ac7790b..a050e760be7 100644 --- a/packages/smooth_app/lib/l10n/app_ur.arb +++ b/packages/smooth_app/lib/l10n/app_ur.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "تصویر لیں", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_uz.arb b/packages/smooth_app/lib/l10n/app_uz.arb index 069f9385023..a10381b1069 100644 --- a/packages/smooth_app/lib/l10n/app_uz.arb +++ b/packages/smooth_app/lib/l10n/app_uz.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Suratga olish", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Toifalar", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Maʻlumot", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_ve.arb b/packages/smooth_app/lib/l10n/app_ve.arb index 276b5888a6a..1e534e3b6c4 100644 --- a/packages/smooth_app/lib/l10n/app_ve.arb +++ b/packages/smooth_app/lib/l10n/app_ve.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_vi.arb b/packages/smooth_app/lib/l10n/app_vi.arb index 00e1b988a3d..2f38a26f8d9 100644 --- a/packages/smooth_app/lib/l10n/app_vi.arb +++ b/packages/smooth_app/lib/l10n/app_vi.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Ảnh mặt trước sản phẩm", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Xác nhận tải lên ảnh trước của Sản phẩm", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ảnh thành phần", "nutritional_facts_photo_title": "Ảnh chụp thông tin dinh dưỡng", "recycling_photo_title": "Ảnh quy định tái chế", + "take_photo_title": "Chụp ảnh", "take_more_photo_title": "Chụp thêm ảnh", "front_photo_uploaded": "Đã tải ảnh mặt trước lên", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "Không tìm thấy sản phẩm", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "không tìm thấy:", - "searchPanelHeader": "Tìm kiếm hoặc quét sản phẩm đầu tiên của bạn", - "@Product query status": {}, "refreshing_product": "Làm mới sản phẩm", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Không thể nạp thông tin về sản phẩm này do lỗi kết nối mạng.", "cached_results_from": "Hiển thị kết quả từ:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "Chúng tôi đã phát mình\nứng dụng quét cộng tác\nvào năm 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "Khi tròn 10 năm,\nchúng tôi đã phát minh lại nó\ntừ đầu!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Tiếp tục", "onboarding_welcome_loading_dialog_title": "Đang tải lên sản phẩm mẫu đầu tiên của bạn", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Rung & Phản hồi", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Hoàn thành thông tin cơ bản", "not_implemented_snackbar_text": "Chưa thực hiện", "category_picker_page_appbar_text": "Thể loại", - "edit_ingredients_extrait_ingredients_btn_text": "Trích xuất thành phần", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Làm mới ảnh", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Xuất thông tin từ bao bì", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Làm mới ảnh", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Không tìm thấy sản phẩm", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Xong", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Dữ liệu", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Loại trừ Điểm sinh thái", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "Tất cả các sản phẩm sắp hoàn thiện", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Điểm sinh thái", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Chia sẻ", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_wa.arb b/packages/smooth_app/lib/l10n/app_wa.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_wa.arb +++ b/packages/smooth_app/lib/l10n/app_wa.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_wo.arb b/packages/smooth_app/lib/l10n/app_wo.arb index 276b5888a6a..1e534e3b6c4 100644 --- a/packages/smooth_app/lib/l10n/app_wo.arb +++ b/packages/smooth_app/lib/l10n/app_wo.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_xh.arb b/packages/smooth_app/lib/l10n/app_xh.arb index d19da9861a3..cc3ec58afc4 100644 --- a/packages/smooth_app/lib/l10n/app_xh.arb +++ b/packages/smooth_app/lib/l10n/app_xh.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_yi.arb b/packages/smooth_app/lib/l10n/app_yi.arb index 0582593abcc..d880cf68188 100644 --- a/packages/smooth_app/lib/l10n/app_yi.arb +++ b/packages/smooth_app/lib/l10n/app_yi.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more pictures", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_yo.arb b/packages/smooth_app/lib/l10n/app_yo.arb index c1fa8eb30ab..c1cd3ac71e0 100644 --- a/packages/smooth_app/lib/l10n/app_yo.arb +++ b/packages/smooth_app/lib/l10n/app_yo.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Ya aworan kan", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Awon eka", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_zh.arb b/packages/smooth_app/lib/l10n/app_zh.arb index 2c13ce99dc0..4dd46edf3f7 100644 --- a/packages/smooth_app/lib/l10n/app_zh.arb +++ b/packages/smooth_app/lib/l10n/app_zh.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "配料表照片", "nutritional_facts_photo_title": "营养成分表", "recycling_photo_title": "循环利用照片", + "take_photo_title": "選一張圖片", "take_more_photo_title": "拍攝更多照片", "front_photo_uploaded": "已上传正面照片", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "没有找到的产品", "@no_product_found": {}, + "no_location_found": "No location found", "not_found": "未找到:", - "searchPanelHeader": "搜索或扫描您的第一个产品", - "@Product query status": {}, "refreshing_product": "产品更新中", "@refreshing_product": { "description": "Confirmation, that the product data of a cached product is queried again" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "由于网络错误,无法获取有关此产品的信息。", "cached_results_from": "显示结果来自:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "我们在 2012 年发明了\n协作\n扫描应用程序", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "当我们 10 岁时,\n我们正在从头开始重新改变它\n!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "继续", "onboarding_welcome_loading_dialog_title": "正在加载您的第一个产品", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "振动 触觉", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "完善基本信息", "not_implemented_snackbar_text": "尚未生效", "category_picker_page_appbar_text": "類別", - "edit_ingredients_extrait_ingredients_btn_text": "提取成分", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "刷新照片", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "提取包装信息", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "刷新照片", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "完成", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "數據", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "排除 Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "所有待完善产品", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "开始刷新本地存储的所有产品", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "推荐给好友", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/l10n/app_zu.arb b/packages/smooth_app/lib/l10n/app_zu.arb index 658fdff9e3d..0fc7d7f6c74 100644 --- a/packages/smooth_app/lib/l10n/app_zu.arb +++ b/packages/smooth_app/lib/l10n/app_zu.arb @@ -566,6 +566,10 @@ "@new_product_dialog_description": { "description": "Please keep it short, like less than 100 characters. Explanatory text of the dialog when the user searched for an unknown barcode." }, + "new_product_dialog_illustration_description": "An illustration with unknown Nutri-Score and Eco-Score", + "@new_product_dialog_illustration_description": { + "description": "A description for accessibility of two images side by side: a Nutri-Score and an EcoScore." + }, "front_packaging_photo_button_label": "Front packaging photo", "@front_packaging_photo_button_label": {}, "confirm_front_packaging_photo_button_label": "Confirm upload of Front packaging photo", @@ -624,6 +628,7 @@ "ingredients_photo_title": "Ingredients Photo", "nutritional_facts_photo_title": "Nutrition Facts Photo", "recycling_photo_title": "Recycling Photo", + "take_photo_title": "Take a picture", "take_more_photo_title": "Take more photos", "front_photo_uploaded": "Front photo uploaded", "@front_photo_uploaded": {}, @@ -735,9 +740,8 @@ "@view_more_photo_button": {}, "no_product_found": "No product found", "@no_product_found": {}, + "no_location_found": "No location 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" @@ -746,10 +750,51 @@ "@product_refreshed": { "description": "Confirmation, that the product data refresh is done" }, + "product_image_accessibility_label": "Image taken on {date}", + "@product_image_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "product_image_outdated_accessibility_label": "Image taken on {date}. This image may be outdated", + "@product_image_outdated_accessibility_label": { + "placeholders": { + "date": { + "type": "String", + "description": "The date of picture (in localized format for YYYY-MM-DD)" + } + } + }, + "homepage_main_card_logo_description": "Welcome to Open Food Facts", + "@homepage_main_card_logo_description": { + "description": "Description for accessibility of the Open Food Facts logo on the homepage" + }, + "homepage_main_card_subheading": "**Scan** a barcode or\n**search** for a product", + "@homepage_main_card_subheading": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please keep it." + }, + "homepage_main_card_search_field_hint": "Search for a product", + "homepage_main_card_search_field_tooltip": "Start search", + "@homepage_main_card_search_field_tooltip": { + "description": "Description for accessibility of the search field on the homepage" + }, + "scan_tagline_news_item_accessibility": "Latest news: {news_title}", + "@scan_tagline_news_item_accessibility": { + "description": "Accessibility label for the title of a news", + "placeholders": { + "news_title": { + "type": "String" + } + } + }, "tagline_app_review": "Do you like the app?", "tagline_app_review_button_positive": "I love it! 😍", "tagline_app_review_button_negative": "Not really…", "tagline_app_review_button_later": "Ask me later", + "tagline_feed_news_button": "Know more", "app_review_negative_modal_title": "You don't like our app?", "app_review_negative_modal_text": "Could you take a few seconds to tell us why?", "app_review_negative_modal_positive_button": "Yes, absolutely!", @@ -758,6 +803,16 @@ "@could_not_refresh": { "description": "The product data couldn't be refreshed" }, + "product_internet_error_modal_title": "An error has occurred!", + "product_internet_error_modal_message": "We are unable to fetch information about this product due to a network error. Please check your internet connection and try again.\n\nInternal error:\n{error}", + "@product_internet_error_modal_message": { + "placeholders": { + "error": { + "type": "String", + "description": "The error message" + } + } + }, "product_internet_error": "Impossible to fetch information about this product due to a network error.", "cached_results_from": "Show results from:", "@cached_results_from": { @@ -798,6 +853,28 @@ "@country_chooser_label": { "description": "Label shown above a selector where the user can select their country (in the preferences)" }, + "currency_chooser_label": "Please choose a currency", + "@currency_chooser_label": { + "description": "Label shown above a selector where the user can select their currency (in the preferences)" + }, + "country_change_message": "You have just changed countries.", + "@country_change_message": { + "description": "Message stating the change of countries" + }, + "currency_auto_change_message": "Do you want to change the currency from {previousCurrency} to {possibleCurrency}?", + "@currency_auto_change_message": { + "description": "Message asking to confirm the change of currencies", + "placeholders": { + "previousCurrency": { + "type": "String", + "description": "Current currency" + }, + "possibleCurrency": { + "type": "String", + "description": "Possible currency" + } + } + }, "onboarding_country_chooser_label": "Please choose a country:", "@onboarding_country_chooser_label": { "description": "The label shown above a selector where the user can select their country (in the onboarding)" @@ -1042,14 +1119,12 @@ } } }, - "onboarding_reinventing_text1": "We invented\nthe collaborative\nscanning app in 2012", - "@onboarding_reinventing_text1": { - "description": "Onboarding / Reinventing page: text 1/2. If possible, balanced on 3 lines." - }, - "onboarding_reinventing_text2": "As we turn 10,\nwe're reinventing it\nfrom the ground up!", - "@onboarding_reinventing_text2": { - "description": "Onboarding / Reinventing page: text 2/2. If possible, balanced on 3 lines." + "onboarding_home_welcome_text1": "Welcome !", + "onboarding_home_welcome_text2": "The app that helps you choose food that is good for **you** and the **planet**!", + "@onboarding_home_welcome_text2": { + "description": "Onboarding home screen welcome text, text surrounded by * will be bold" }, + "onboarding_continue_button": "Continue", "onboarding_welcome_loading_dialog_title": "Loading your first example product", "@onboarding_welcome_loading_dialog_title": { "description": "Title for the onboarding loading dialog" @@ -1160,6 +1235,10 @@ "@camera_play_sound_subtitle": { "description": "SubTitle for the Camera play sound toggle" }, + "camera_window_accessibility_label": "Scan a barcode with your camera", + "@camera_window_accessibility_label": { + "description": "Accessibility label for the camera window" + }, "app_haptic_feedback_title": "Vibration & Haptics", "@app_haptic_feedback_title": { "description": "Title for the Haptic feedback toggle" @@ -1400,18 +1479,34 @@ "completed_basic_details_btn_text": "Complete basic details", "not_implemented_snackbar_text": "Not implemented yet", "category_picker_page_appbar_text": "Categories", - "edit_ingredients_extrait_ingredients_btn_text": "Extract ingredients", - "@edit_ingredients_extrait_ingredients_btn_text": { + "edit_ingredients_extract_ingredients_btn_text": "Extract ingredients from the photo", + "@edit_ingredients_extract_ingredients_btn_text": { "description": "Ingredients edition - Extract ingredients" }, + "edit_ingredients_extracting_ingredients_btn_text": "Extracting ingredients\nfrom the photo", + "@edit_ingredients_extracting_ingredients_btn_text": { + "description": "Ingredients edition - Extracting ingredients" + }, + "edit_ingredients_loading_photo_btn_text": "Loading photo…", + "@edit_ingredients_loading_photo_btn_text": { + "description": "Ingredients edition - Loading photo from the server" + }, "edit_ingredients_refresh_photo_btn_text": "Refresh photo", "@edit_ingredients_refresh_photo_btn_text": { "description": "Ingredients edition - Refresh photo" }, - "edit_packaging_extract_btn_text": "Extract packaging", + "edit_packaging_extract_btn_text": "Extract packaging\nfrom the photo", "@edit_packaging_extract_btn_text": { "description": "Packaging edition - OCR-Extract packaging" }, + "edit_packaging_extracting_btn_text": "Extracting packaging from the photo", + "@edit_packaging_extracting_btn_text": { + "description": "Packaging edition - OCR-Extracting packaging" + }, + "edit_packaging_loading_photo_btn_text": "Loading photo…", + "@edit_packaging_loading_photo_btn_text": { + "description": "Packaging edition - Loading photo from the server" + }, "edit_packaging_refresh_photo_btn_text": "Refresh photo", "@edit_packaging_refresh_photo_btn_text": { "description": "Packaging edition - Refresh photo" @@ -1623,14 +1718,128 @@ }, "prices_app_dev_mode_flag": "Shortcut to Prices app on product page", "prices_app_button": "Go to Prices app", + "prices_generic_title": "Prices", + "prices_add_n_prices": "{count,plural, =1{Add a price} other{App {count} prices}}", + "prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}", + "prices_add_an_item": "Add an item", + "prices_add_a_price": "Add a price", + "prices_add_a_receipt": "Add a receipt", + "prices_add_price_tags": "Add price tags", + "prices_barcode_search_not_found": "Product not found", + "prices_barcode_search_none_yet": "No product yet", + "prices_barcode_search_question": "Do you want to look for this product?", + "prices_barcode_search_title": "Product search", + "prices_barcode_search_running": "Looking for {barcode}", + "@prices_barcode_search_running": { + "description": "Dialog title about barcode look-up", + "placeholders": { + "barcode": { + "type": "String" + } + } + }, + "prices_barcode_reader_action": "Barcode reader", + "prices_view_prices": "View the prices", + "prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}", + "@prices_list_length_one_page": { + "description": "Number of prices for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_list_length_many_pages": "Latest {pageSize} prices (total: {total})", + "@prices_list_length_many_pages": { + "description": "Number of prices for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_proofs_list_length_one_page": "{count,plural, =0{No proof yet} =1{Only one proof} other{All {count} proofs}}", + "@prices_proofs_list_length_one_page": { + "description": "Number of proofs for one-page result", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "prices_proofs_list_length_many_pages": "Latest {pageSize} proofs (total: {total})", + "@prices_proofs_list_length_many_pages": { + "description": "Number of proofs for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_users_list_length_many_pages": "Top {pageSize} contributors (total: {total})", + "@prices_users_list_length_many_pages": { + "description": "Number of users for one-page result", + "placeholders": { + "pageSize": { + "type": "int" + }, + "total": { + "type": "int" + } + } + }, + "prices_amount_subtitle": "Amount", + "prices_amount_is_discounted": "Is discounted?", + "prices_amount_price_normal": "Price", + "prices_amount_price_discounted": "Discounted price", + "prices_amount_price_not_discounted": "Original price", + "prices_amount_no_product": "One product is missing!", + "prices_amount_price_incorrect": "Incorrect value", + "prices_amount_price_mandatory": "Mandatory value", + "prices_currency_subtitle": "Currency", + "prices_date_subtitle": "Date", + "prices_location_subtitle": "Shop", + "prices_location_find": "Find a shop", + "prices_location_mandatory": "You need to select a shop!", + "prices_proof_subtitle": "Proof", + "prices_proof_find": "Select a proof", + "prices_proof_receipt": "Receipt", + "prices_proof_price_tag": "Price tag", + "prices_proof_mandatory": "You need to select a proof!", + "prices_add_validation_error": "Validation error", + "prices_privacy_warning_title": "Privacy warning", + "prices_privacy_warning_message": "Prices will be public, along with the store they refer to.\nThat might allow people who know about your Open Food Facts pseudonym to:\n* infer in which area you live\n* know what you are buying\nIf you are uneasy with that, please change your pseudonym, or create a new Open Food Facts account and log into the app with it.", + "prices_unknown_product": "Unknown product", + "@prices_unknown_product": { + "description": "Very small text, in the context of prices, to say that the product is unknown" + }, "dev_preferences_import_history_result_success": "Done", "@dev_preferences_import_history_result_success": { "description": "User dev preferences - Import history - Result successful" }, + "dev_mode_section_server": "Server configuration", + "dev_mode_section_product_page": "Product page", + "dev_mode_section_ui": "User Interface", + "dev_mode_section_data": "Data", + "dev_mode_section_experimental_features": "Experimental features", "dev_mode_hide_ecoscore_title": "Exclude Eco-Score", "@dev_mode_hide_ecoscore_title": { "description": "User dev preferences - Disable Ecoscore - Title" }, + "dev_mode_spellchecker_for_ocr_title": "Use a spellchecker for OCR screens", + "@dev_mode_spellchecker_for_ocr_title": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Title" + }, + "dev_mode_spellchecker_for_ocr_subtitle": "(Ingredients and packaging)", + "@dev_mode_spellchecker_for_ocr_subtitle": { + "description": "User dev preferences - Enable Spellchecker on OCR screens - Subtitle" + }, "search_history_item_edit_tooltip": "Reuse and edit this search", "@search_history_item_edit_tooltip": { "description": "A tooltip to explain the Pen button near a search term -> it allows to reuse the item" @@ -1682,6 +1891,38 @@ "@user_search_to_be_completed_title": { "description": "User search (to be completed): list tile title" }, + "user_search_prices_title": "My prices", + "@user_search_prices_title": { + "description": "User prices: list tile title" + }, + "user_search_proofs_title": "My proofs", + "@user_search_proofs_title": { + "description": "User proofs: list tile title" + }, + "user_search_proof_title": "My proof", + "@user_search_proof_title": { + "description": "User proof: page title" + }, + "user_any_search_prices_title": "Contributor prices", + "@user_any_search_prices_title": { + "description": "User prices (everybody except me): list tile title" + }, + "all_search_prices_latest_title": "Latest Prices added", + "@all_search_prices_latest_title": { + "description": "Latest prices: list tile title" + }, + "all_search_prices_top_user_title": "Top price contributors", + "@all_search_prices_top_user_title": { + "description": "Top price users: list tile title" + }, + "all_search_prices_top_location_title": "Stores with the most prices", + "@all_search_prices_top_location_title": { + "description": "Top price locations: list tile title" + }, + "all_search_prices_top_product_title": "Products with the most prices", + "@all_search_prices_top_product_title": { + "description": "Top price products: list tile title" + }, "all_search_to_be_completed_title": "All to-be-completed products", "@all_search_to_be_completed_title": { "description": "All products to be completed: list tile title" @@ -1948,6 +2189,10 @@ "@image_upload_queued": { "description": "Message when a photo is queued for upload" }, + "add_price_queued": "The price will be sent to the server as soon as possible.", + "@add_price_queued": { + "description": "Message when an added price is queued for the server" + }, "background_task_title_full_refresh": "Starting the refresh of all the products locally stored", "@background_task_title_full_refresh": { "description": "Snackbar message when a full refresh is started" @@ -2462,6 +2707,10 @@ "@country_selector_title": { "description": "Label written as the title of the dialog to select the user country" }, + "currency_selector_title": "Select your currency:", + "@currency_selector_title": { + "description": "Label written as the title of the dialog to select the user currency" + }, "language_selector_title": "Select your language:", "@language_selector_title": { "description": "Label written as the title of the dialog to select the user language" @@ -2488,8 +2737,20 @@ "nutriscore_c": "Nutri-Score C", "nutriscore_d": "Nutri-Score D", "nutriscore_e": "Nutri-Score E", + "nutriscore_new_formula": "Nutri-Score {letter} (New calculation)", + "@nutriscore_new_formula": { + "description": "A generic string to define a Nutri-Score V2 with a letter [eg: \"Nutri-Score A (New calculation)\"]", + "placeholders": { + "letter": { + "type": "String" + } + } + }, + "nutriscore_new_formula_title": "Nutri-Score (New calculation)", "nutriscore_unknown": "Unknown Nutri-Score", + "nutriscore_unknown_new_formula": "Unknown Nutri-Score (New calculation)", "nutriscore_not_applicable": "Nutri-Score is not applicable", + "nutriscore_not_applicable_new_formula": "Nutri-Score is not applicable (New calculation)", "ecoscore_generic": "Eco-Score", "ecoscore_a": "Eco-Score A", "ecoscore_b": "Eco-Score B", @@ -2510,6 +2771,7 @@ "faq_title_install_beauty": "Install Open Beauty Facts to create a cosmetic database", "faq_title_install_pet": "Install Open Pet Food Facts to create a pet food database", "faq_title_install_product": "Install Open Products Facts to create a products database to extend the life of objects", + "faq_nutriscore_nutriscore": "New calculation of the Nutri-Score: what's new?", "contact_title_pro_page": "Pro? Import your products in Open Food Facts", "contact_title_pro_email": "Producer Contact", "contact_title_press_page": "Press Page", @@ -2526,5 +2788,84 @@ "link_cant_be_opened": "This link can't be opened on your device. Please check that you have a browser installed.", "@link_cant_be_opened": { "description": "An error may happen if the device doesn't have a browser installed." + }, + "knowledge_panel_page_title_no_title": "Details for {productName}", + "@knowledge_panel_page_title_no_title": { + "description": "The title of the page when we click on an item in the product page and this page is unnamed", + "placeholders": { + "productName": { + "type": "String" + } + } + }, + "knowledge_panel_page_title": "Details for {pageName} with {productName}", + "@knowledge_panel_page_title": { + "description": "The title of the page when we click on an item in the product page", + "placeholders": { + "pageName": { + "type": "String" + }, + "productName": { + "type": "String" + } + } + }, + "guide_title": "Guide", + "@guide_title": { + "description": "A title for a guide" + }, + "guide_share_label": "Share", + "guide_nutriscore_v2_enabled": "true", + "@guide_nutriscore_v2_enabled": { + "description": "Please NEVER touch this value and let the OFF team change it!" + }, + "guide_nutriscore_v2_title": "The Nutri-Score is evolving: explanations!", + "@guide_nutriscore_v2_title": { + "description": "The title of the guide (please don't forget the use of non-breaking spaces)" + }, + "guide_nutriscore_v2_file_language": "en", + "@guide_nutriscore_v2_file_language": { + "description": "The logo is only available in de/en/fr/ln/nl. Please use en if not available (in lowercase, please)." + }, + "guide_nutriscore_v2_what_is_nutriscore_title": "What is the Nutri-Score?", + "guide_nutriscore_v2_what_is_nutriscore_paragraph1": "The Nutri-Score is a logo which aims to inform you about the **nutritional quality of foods**.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph1": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_what_is_nutriscore_paragraph2": "The color code varies from dark green (**A**) for the **healthiest** products to dark red (**E**) for the **less healthy** ones.", + "@guide_nutriscore_v2_what_is_nutriscore_paragraph2": { + "description": "Text between asterisks (eg: **My Text**) means text in bold. Please try to keep it." + }, + "guide_nutriscore_v2_nutriscore_a_caption": "The Nutri-Score A logo", + "guide_nutriscore_v2_why_v2_title": "Why is Nutri-Score evolving?", + "guide_nutriscore_v2_why_v2_intro": "The Nutri-Score formula **is evolving** to provide better recommendations:", + "guide_nutriscore_v2_why_v2_arg1_title": "Better evaluate all drinks", + "guide_nutriscore_v2_why_v2_arg1_text": "The comparative notes of **milk**, **dairy drinks** with added sugar and **vegetable** drinks were better differentiated in the new algorithm.", + "guide_nutriscore_v2_why_v2_arg2_title": "Better ranking of drinks", + "guide_nutriscore_v2_why_v2_arg2_text": "The **sugar content** is better taken into account and favors **lowly sweetened** drinks.\\n**Sweeteners will also be penalized**: diet sodas will be downgraded from a B rating to between C and E. Water remains the recommended drink.", + "guide_nutriscore_v2_why_v2_arg3_title": "Salt and sugar penalized", + "guide_nutriscore_v2_why_v2_arg3_text": "Products **too sweet** or **too salty** will see their **rating further downgraded**.", + "guide_nutriscore_v2_why_v2_arg4_title": "Hierarchy within oils and fishes", + "guide_nutriscore_v2_why_v2_arg4_text": "The rating of certain **fatty fish** and **oils rich in good fats** will improve.", + "guide_nutriscore_v2_why_v2_arg5_title": "Limit red meat", + "guide_nutriscore_v2_why_v2_arg5_text": "Consumption of **red meat should be limited**. This is why **poultry will be comparatively better ranked**.", + "guide_nutriscore_v2_new_logo_title": "How to differentiate old Nutri-Score and new calculation?", + "guide_nutriscore_v2_new_logo_text": "From now on, the logo can display a mention \"**New calculation**\" to clarify that this is indeed the new calculation.", + "guide_nutriscore_v2_new_logo_image_caption": "The logo of the new Nutri-Score", + "guide_nutriscore_v2_where_title": "Where to find the new Nutri-Score calculation?", + "guide_nutriscore_v2_where_paragraph1": "The Nutri-Score is applied in several countries: Germany, Belgium, Spain, France, Luxembourg, the Netherlands and Switzerland.", + "guide_nutriscore_v2_where_paragraph2": "Manufacturers have until **2026** at the latest **to replace** the old calculation with the new one.", + "guide_nutriscore_v2_where_paragraph3": "Without waiting, you **will already find in the OpenFoodFacts application**, the new calculation, including if the manufacturers have not updated the score.", + "guide_nutriscore_v2_unchanged_title": "What doesn't change", + "guide_nutriscore_v2_unchanged_paragraph1": "The Nutri-Score is a score designed to **measure nutritional quality**. It is **complementary to the NOVA group** on **ultra-processed foods** (also present in the application).", + "guide_nutriscore_v2_unchanged_paragraph2": "For manufacturers, the display of the Nutri-Score **remains optional**.", + "guide_nutriscore_v2_share_link": "https://world.openfoodfacts.org/nutriscore-v2", + "guide_nutriscore_v2_share_message": "", + "@guide_nutriscore_v2_share_message": { + "description": "Please let empty for now (maybe use in the future)" + }, + "preview_badge": "Preview", + "@preview_badge": { + "description": "Badge to indicate that the product is in preview mode (Be careful with this translation)" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/main.dart b/packages/smooth_app/lib/main.dart index 8c4e1686d58..f2577ae069e 100644 --- a/packages/smooth_app/lib/main.dart +++ b/packages/smooth_app/lib/main.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:app_store_shared/app_store_shared.dart'; import 'package:dart_ping_ios/dart_ping_ios.dart'; -import 'package:device_preview/device_preview.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -17,6 +16,7 @@ import 'package:provider/single_child_widget.dart'; import 'package:scanner_shared/scanner_shared.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:smooth_app/data_models/continuous_scan_model.dart'; +import 'package:smooth_app/data_models/news_feed/newsfeed_provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/data_models/user_management_provider.dart'; @@ -81,14 +81,22 @@ Future launchSmoothApp({ WidgetsFlutterBinding.ensureInitialized(); FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + _enableEdgeToEdgeMode(); + if (kReleaseMode) { await AnalyticsHelper.initSentry( appRunner: () => runApp(const SmoothApp())); } else { - runApp( - DevicePreview( - enabled: true, - builder: (_) => const SmoothApp(), + runApp(const SmoothApp()); + } +} + +void _enableEdgeToEdgeMode() { + if (Platform.isAndroid) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, ), ); } @@ -136,6 +144,7 @@ Future _init1() async { ), daoString: DaoString(_localDatabase), ); + ProductQuery.setQueryType(_userPreferences); UserManagementProvider().checkUserLoginValidity(); await AnalyticsHelper.linkPreferences(_userPreferences); @@ -144,7 +153,6 @@ Future _init1() async { _themeProvider = ThemeProvider(_userPreferences); _colorProvider = ColorProvider(_userPreferences); _textContrastProvider = TextContrastProvider(_userPreferences); - ProductQuery.setQueryType(_userPreferences); await CameraHelper.init(); await ProductQuery.setUuid(_localDatabase); @@ -205,8 +213,12 @@ class _SmoothAppState extends State { // The `create` constructor of [ChangeNotifierProvider] takes care of // disposing the value. - ChangeNotifierProvider provide(T value) => - ChangeNotifierProvider(create: (BuildContext context) => value); + ChangeNotifierProvider provide(T value, + {bool? lazy}) => + ChangeNotifierProvider( + create: (BuildContext context) => value, + lazy: lazy, + ); if (!_screenshots) { // ending FlutterNativeSplash.preserve() @@ -225,13 +237,19 @@ class _SmoothAppState extends State { provide(_continuousScanModel), provide(_permissionListener), ], - child: AnimationsLoader( - child: AppNavigator( - observers: [ - SentryNavigatorObserver(), - matomoObserver, - ], - child: Builder(builder: _buildApp), + child: ChangeNotifierProvider( + create: (BuildContext context) => AppNewsProvider( + context.read(), + ), + lazy: true, + child: AnimationsLoader( + child: AppNavigator( + observers: [ + SentryNavigatorObserver(), + matomoObserver, + ], + child: Builder(builder: _buildApp), + ), ), ), ); diff --git a/packages/smooth_app/lib/pages/crop_helper.dart b/packages/smooth_app/lib/pages/crop_helper.dart new file mode 100644 index 00000000000..59d5e1de688 --- /dev/null +++ b/packages/smooth_app/lib/pages/crop_helper.dart @@ -0,0 +1,87 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui' as ui; + +import 'package:crop_image/crop_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/background/background_task_image.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; + +/// Crop Helper for images in crop page: process to run when cropping an image. +abstract class CropHelper { + /// Is that a new image, or an already cropped one? + bool isNewImage(); + + /// Page title of the crop page. + String getPageTitle(final AppLocalizations appLocalizations); + + /// Icon of the "process!" button. + IconData getProcessIcon(); + + /// Label of the "process!" button. + String getProcessLabel(final AppLocalizations appLocalizations); + + /// Processes the crop operation. + Future process({ + required final BuildContext context, + required final CropController controller, + required final ui.Image image, + required final File inputFile, + required final File smallCroppedFile, + required final Directory directory, + required final int sequenceNumber, + final List? offsets, + }); + + /// Should we display the eraser with the crop grid? + bool get enableEraser; + + /// Returns the crop rect according to local cropping method * factor. + @protected + Rect getLocalCropRect(final CropController controller) => + BackgroundTaskImage.getUpsizedRect(controller.crop); + + @protected + CropParameters getCropParameters({ + required final CropController controller, + required final File? fullFile, + required final File smallCroppedFile, + final List? offsets, + }) { + final Rect cropRect = getLocalCropRect(controller); + final List eraserCoordinates = []; + if (offsets != null) { + for (final Offset offset in offsets) { + eraserCoordinates.add(offset.dx); + eraserCoordinates.add(offset.dy); + } + } + return CropParameters( + fullFile: fullFile, + smallCroppedFile: smallCroppedFile, + rotation: controller.rotation.degrees, + x1: cropRect.left.ceil(), + y1: cropRect.top.ceil(), + x2: cropRect.right.floor(), + y2: cropRect.bottom.floor(), + eraserCoordinates: eraserCoordinates, + ); + } + + /// Returns a copy of a file with the full image (no cropping here). + /// + /// To be sent to the server, as well as the crop parameters and the rotation. + /// It's faster for us to let the server do the actual cropping full size. + @protected + Future copyFullImageFile( + final Directory directory, + final int sequenceNumber, + final File inputFile, + ) async { + final File result; + final String fullPath = '${directory.path}/full_image_$sequenceNumber.jpeg'; + result = inputFile.copySync(fullPath); + return result; + } +} diff --git a/packages/smooth_app/lib/pages/crop_page.dart b/packages/smooth_app/lib/pages/crop_page.dart index df4fb4bcf62..e7a832c5c71 100644 --- a/packages/smooth_app/lib/pages/crop_page.dart +++ b/packages/smooth_app/lib/pages/crop_page.dart @@ -8,10 +8,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; -import 'package:smooth_app/background/background_task_crop.dart'; import 'package:smooth_app/background/background_task_image.dart'; import 'package:smooth_app/background/background_task_upload.dart'; -import 'package:smooth_app/data_models/continuous_scan_model.dart'; import 'package:smooth_app/database/dao_int.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; @@ -20,7 +18,10 @@ import 'package:smooth_app/generic_lib/loading_dialog.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/database_helper.dart'; import 'package:smooth_app/helpers/image_compute_container.dart'; -import 'package:smooth_app/helpers/image_field_extension.dart'; +import 'package:smooth_app/pages/crop_helper.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; +import 'package:smooth_app/pages/prices/eraser_model.dart'; +import 'package:smooth_app/pages/prices/eraser_painter.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/edit_image_button.dart'; import 'package:smooth_app/pages/product/may_exit_page_helper.dart'; @@ -32,12 +33,9 @@ import 'package:smooth_app/widgets/will_pop_scope.dart'; class CropPage extends StatefulWidget { const CropPage({ required this.inputFile, - required this.barcode, - required this.imageField, - required this.language, required this.initiallyDifferent, + required this.cropHelper, required this.isLoggedInMandatory, - this.imageId, this.initialCropRect, this.initialRotation, }); @@ -45,22 +43,17 @@ class CropPage extends StatefulWidget { /// The initial input file we start with. final File inputFile; - final ImageField imageField; - final String barcode; - final OpenFoodFactsLanguage language; - /// Is the full picture initially different from the current selection? final bool initiallyDifferent; - /// Only makes sense when we deal with an "already existing" image. - final int? imageId; - final Rect? initialCropRect; final CropRotation? initialRotation; final bool isLoggedInMandatory; + final CropHelper cropHelper; + @override State createState() => _CropPageState(); } @@ -83,6 +76,13 @@ class _CropPageState extends State { late Rect _initialCrop; late CropRotation _initialRotation; + late Uint8List _data; + + /// True if we switched to the "erase" mode, and not the "crop grid" mode. + bool _isErasing = false; + + final EraserModel _eraserModel = EraserModel(); + Future _load(final Uint8List list) async { _image = await BackgroundTaskImage.loadUiImage(list); _initialCrop = _getInitialRect(); @@ -146,20 +146,23 @@ class _CropPageState extends State { _initLoad(); } - Future _initLoad() async => _load(await widget.inputFile.readAsBytes()); + Future _initLoad() async { + _data = await widget.inputFile.readAsBytes(); + await _load(_data); + } @override Widget build(final BuildContext context) { - _screenSize = MediaQuery.of(context).size; + _screenSize = MediaQuery.sizeOf(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); return WillPopScope2( - onWillPop: () async => (await _mayExitPage(saving: false), null), + onWillPop: _onWillPop, child: SmoothScaffold( appBar: SmoothAppBar( centerTitle: false, titleSpacing: 0.0, title: Text( - widget.imageField.getImagePageTitle(appLocalizations), + widget.cropHelper.getPageTitle(appLocalizations), maxLines: 2, ), ), @@ -176,39 +179,120 @@ class _CropPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _IconButton( - iconData: Icons.rotate_90_degrees_ccw_outlined, - onPressed: () => setState( - () => _controller.rotateLeft(), - ), - ), - _IconButton( - iconData: Icons.rotate_90_degrees_cw_outlined, - onPressed: () => setState( - () => _controller.rotateRight(), - ), - ), - ], + Padding( + padding: const EdgeInsetsDirectional.only( + top: SMALL_SPACE, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (!_isErasing) + _IconButton( + iconData: Icons.rotate_90_degrees_ccw_outlined, + onPressed: () => setState( + () { + _controller.rotateLeft(); + _eraserModel.rotation = _controller.rotation; + }, + ), + ), + if (widget.cropHelper.enableEraser) + _IconButton( + iconData: _isErasing ? Icons.crop : Icons.brush, + color: _isErasing ? null : EraserPainter.color, + onPressed: () => setState( + () => _isErasing = !_isErasing, + ), + ), + if (_isErasing) + _IconButton( + iconData: Icons.undo, + onPressed: _eraserModel.isEmpty + ? null + : () => setState( + () => _eraserModel.undo(), + ), + ), + if (!_isErasing) + _IconButton( + iconData: Icons.rotate_90_degrees_cw_outlined, + onPressed: () => setState( + () { + _controller.rotateRight(); + _eraserModel.rotation = _controller.rotation; + }, + ), + ), + ], + ), ), Expanded( - child: CropImage( - controller: _controller, - image: Image.file(widget.inputFile), - minimumImageSize: MINIMUM_TOUCH_SIZE, - gridCornerSize: MINIMUM_TOUCH_SIZE * .75, - touchSize: MINIMUM_TOUCH_SIZE, - paddingSize: MINIMUM_TOUCH_SIZE * .5, - alwaysMove: true, + child: Stack( + children: [ + IgnorePointer( + ignoring: _isErasing, + child: CropImage( + controller: _controller, + image: Image.memory(_data), + minimumImageSize: MINIMUM_TOUCH_SIZE, + gridCornerSize: MINIMUM_TOUCH_SIZE * .75, + touchSize: MINIMUM_TOUCH_SIZE, + paddingSize: MINIMUM_TOUCH_SIZE * .5, + alwaysMove: true, + overlayPainter: !widget.cropHelper.enableEraser + ? null + : EraserPainter( + eraserModel: _eraserModel, + ), + ), + ), + if (_isErasing) + LayoutBuilder( + builder: ( + final BuildContext context, + final BoxConstraints constraints, + ) => + Center( + child: GestureDetector( + onPanStart: + (final DragStartDetails details) => + setState( + () => _eraserModel.panStart( + details.localPosition, + constraints, + ), + ), + onPanUpdate: + (final DragUpdateDetails details) => + setState( + () => _eraserModel.panUpdate( + details.localPosition, + constraints, + ), + ), + onPanEnd: (final DragEndDetails details) => + setState( + () => _eraserModel.panEnd(), + ), + ), + ), + ), + ], ), ), - Center( - child: EditImageButton( - iconData: Icons.send, - label: appLocalizations.send_image_button_label, - onPressed: () async => _mayExitPage(saving: true), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: VERY_SMALL_SPACE, + vertical: SMALL_SPACE, + ), + child: SizedBox( + width: double.infinity, + child: EditImageButton.center( + iconData: widget.cropHelper.getProcessIcon(), + label: widget.cropHelper + .getProcessLabel(appLocalizations), + onPressed: () async => _saveImageAndPop(), + ), ), ), ], @@ -218,24 +302,10 @@ class _CropPageState extends State { ); } - /// Returns a file with the full image (no cropping here). - /// - /// To be sent to the server, as well as the crop parameters and the rotation. - /// It's faster for us to let the server do the actual cropping full size. - Future _getFullImageFile( - final Directory directory, - final int sequenceNumber, - ) async { - final File result; - final String fullPath = '${directory.path}/full_image_$sequenceNumber.jpeg'; - result = widget.inputFile.copySync(fullPath); - return result; - } - /// Returns a small file with the cropped image, for the transient image. /// /// Here we use BMP format as it's faster to encode. - Future _getCroppedImageFile( + Future _getSmallCroppedImageFile( final Directory directory, final int sequenceNumber, ) async { @@ -243,8 +313,20 @@ class _CropPageState extends State { final String croppedPath = '${directory.path}/cropped_$sequenceNumber.bmp'; final File result = File(croppedPath); setState(() => _progress = appLocalizations.crop_page_action_cropping); - final ui.Image cropped = await _controller.croppedBitmap( + final ui.Image cropped = await CropController.getCroppedBitmap( + image: _image, maxSize: _screenSize.longestSide, + crop: _controller.crop, + rotation: _controller.rotation, + overlayPainter: !widget.cropHelper.enableEraser + ? null + : EraserPainter( + eraserModel: EraserModel( + rotation: _controller.rotation, + offsets: _eraserModel.offsets, + ), + cropRect: _controller.crop, + ), ); setState(() => _progress = appLocalizations.crop_page_action_local); @@ -259,11 +341,11 @@ class _CropPageState extends State { return result; } - Future _saveFileAndExitTry() async { + Future _saveImageAndExitTry() async { final AppLocalizations appLocalizations = AppLocalizations.of(context); // only for new image upload we have to check the minimum size. - if (widget.imageId == null) { + if (widget.cropHelper.isNewImage()) { // Returns the size of the resulting cropped image. Size getCroppedSize() { switch (_controller.rotation) { @@ -321,7 +403,7 @@ class _CropPageState extends State { await getNextSequenceNumber(daoInt, _CROP_PAGE_SEQUENCE_KEY); final Directory directory = await BackgroundTaskUpload.getDirectory(); - final File croppedFile = await _getCroppedImageFile( + final File smallCroppedFile = await _getSmallCroppedImageFile( directory, sequenceNumber, ); @@ -329,200 +411,114 @@ class _CropPageState extends State { setState( () => _progress = appLocalizations.crop_page_action_server, ); - if (widget.imageId == null) { - // in this case, it's a brand new picture, with crop parameters. - // for performance reasons, we do not crop the image full-size here, - // but in the background task. - // for privacy reasons, we won't send the full image to the server and - // let it crop it: we'll send the cropped image directly. - final File fullFile = await _getFullImageFile( - directory, - sequenceNumber, - ); - final Rect cropRect = _getLocalCropRect(); - if (mounted) { - await BackgroundTaskImage.addTask( - widget.barcode, - language: widget.language, - imageField: widget.imageField, - fullFile: fullFile, - croppedFile: croppedFile, - rotation: _controller.rotation.degrees, - x1: cropRect.left.ceil(), - y1: cropRect.top.ceil(), - x2: cropRect.right.floor(), - y2: cropRect.bottom.floor(), - context: context, - ); - } - } else { - // in this case, it's an existing picture, with crop parameters. - // we let the server do everything: better performance, and no privacy - // issue here (we're cropping from an allegedly already privacy compliant - // picture). - final Rect cropRect = _getServerCropRect(); - if (mounted) { - await BackgroundTaskCrop.addTask( - widget.barcode, - language: widget.language, - imageField: widget.imageField, - imageId: widget.imageId!, - croppedFile: croppedFile, - rotation: _controller.rotation.degrees, - x1: cropRect.left.ceil(), - y1: cropRect.top.ceil(), - x2: cropRect.right.floor(), - y2: cropRect.bottom.floor(), - context: context, - ); - } - } - localDatabase.notifyListeners(); if (!mounted) { - return croppedFile; + return null; } - final ContinuousScanModel model = context.read(); - await model - .onCreateProduct(widget.barcode); // TODO(monsieurtanuki): a bit fishy - - return croppedFile; + return widget.cropHelper.process( + context: context, + controller: _controller, + image: _image, + smallCroppedFile: smallCroppedFile, + directory: directory, + inputFile: widget.inputFile, + sequenceNumber: sequenceNumber, + offsets: _eraserModel.offsets, + ); } - Future _saveFileAndExit() async { + Future _saveImage() async { if (!await ProductRefresher().checkIfLoggedIn( context, isLoggedInMandatory: widget.isLoggedInMandatory, )) { - return false; + return null; } setState( () => _progress = AppLocalizations.of(context).crop_page_action_saving, ); try { - final File? file = await _saveFileAndExitTry(); + final CropParameters? cropParameters = await _saveImageAndExitTry(); _progress = null; - if (file == null) { - if (mounted) { - setState(() {}); - } - return false; - } else { - if (mounted) { - Navigator.of(context).pop(file); - } - return true; + if (mounted) { + setState(() {}); } + return cropParameters; } catch (e) { - _showErrorDialog(); - return false; + await _showErrorDialog(); + return null; } finally { _progress = null; } } - /// Returns the crop rect according to local cropping method * factor. - Rect _getLocalCropRect() => BackgroundTaskImage.getResizedRect( - _controller.crop, BackgroundTaskImage.cropConversionFactor); - - Offset _getRotatedOffsetForOff(final Offset offset) => - _getRotatedOffsetForOffHelper( - _controller.rotation, - offset, - _image.width.toDouble(), - _image.height.toDouble(), - ); + static const String _CROP_PAGE_SEQUENCE_KEY = 'crop_page_sequence'; - /// Returns the offset as rotated, for the OFF-dart rotation/crop tool. - Offset _getRotatedOffsetForOffHelper( - final CropRotation rotation, - final Offset offset01, - final double noonWidth, - final double noonHeight, - ) { - switch (rotation) { - case CropRotation.up: - case CropRotation.down: - return Offset( - noonWidth * offset01.dx, - noonHeight * offset01.dy, - ); - case CropRotation.right: - case CropRotation.left: - return Offset( - noonHeight * offset01.dx, - noonWidth * offset01.dy, - ); + /// Saves the image if relevant after a user click, and pops the result. + Future _saveImageAndPop() async { + if (_nothingHasChanged()) { + // nothing has changed, let's leave + Navigator.of(context).pop(); + return; } - } - /// Returns the crop rect according to server cropping method. - Rect _getServerCropRect() { - final Offset center = _getRotatedOffsetForOff(_controller.crop.center); - final Offset topLeft = _getRotatedOffsetForOff(_controller.crop.topLeft); - double width = 2 * (center.dx - topLeft.dx); - if (width < 0) { - width = -width; - } - double height = 2 * (center.dy - topLeft.dy); - if (height < 0) { - height = -height; + try { + final CropParameters? cropParameters = await _saveImage(); + if (cropParameters != null) { + if (mounted) { + Navigator.of(context).pop(cropParameters); + } + } + } catch (e) { + await _showExceptionDialog(e); } - final Rect rect = Rect.fromCenter( - center: center, - width: width, - height: height, - ); - return rect; } - static const String _CROP_PAGE_SEQUENCE_KEY = 'crop_page_sequence'; + bool _nothingHasChanged() => + _controller.value.rotation == _initialRotation && + _controller.value.crop == _initialCrop && + !widget.initiallyDifferent; - /// Returns `true` if we should really exit the page. - /// - /// Parameter [saving] tells about the context: are we leaving the page, - /// or have we clicked on the "save" button? - Future _mayExitPage({required final bool saving}) async { - if (_controller.value.rotation == _initialRotation && - _controller.value.crop == _initialCrop && - !widget.initiallyDifferent) { + Future<(bool, CropParameters?)> _onWillPop() async { + if (_nothingHasChanged()) { // nothing has changed, let's leave - if (saving) { - Navigator.of(context).pop(); - } - return true; + return (true, null); } // the cropped image has changed, but the user went back without saving - if (!saving) { - final bool? pleaseSave = - await MayExitPageHelper().openSaveBeforeLeavingDialog(context); - if (pleaseSave == null) { - return false; - } - if (pleaseSave == false) { - return true; - } - if (!mounted) { - return false; - } + final bool? pleaseSave = + await MayExitPageHelper().openSaveBeforeLeavingDialog( + context, + title: widget.cropHelper.getPageTitle(AppLocalizations.of(context)), + ); + if (pleaseSave == null) { + return (false, null); + } + if (pleaseSave == false) { + return (true, null); + } + if (!mounted) { + return (false, null); } try { - return _saveFileAndExit(); - } catch (e) { - if (mounted) { - // not likely to happen, but you never know... - await LoadingDialog.error( - context: context, - title: 'Could not prepare picture with exception $e', - ); + final CropParameters? cropParameters = await _saveImage(); + if (cropParameters != null) { + if (mounted) { + return (true, cropParameters); + } } - return false; + } catch (e) { + await _showExceptionDialog(e); } + + return (false, null); } - Future _showErrorDialog() { + Future _showErrorDialog() async { + if (!mounted) { + return; + } final AppLocalizations appLocalizations = AppLocalizations.of(context); return showDialog( @@ -535,6 +531,16 @@ class _CropPageState extends State { }, ); } + + Future _showExceptionDialog(final Object e) async { + if (mounted) { + // not likely to happen, but you never know... + return LoadingDialog.error( + context: context, + title: 'Could not prepare picture with exception $e', + ); + } + } } /// Standard icon button for this page. @@ -542,15 +548,20 @@ class _IconButton extends StatelessWidget { const _IconButton({ required this.iconData, required this.onPressed, + this.color, }); final IconData iconData; - final VoidCallback onPressed; + final VoidCallback? onPressed; + final Color? color; @override Widget build(BuildContext context) => ElevatedButton( onPressed: onPressed, style: ElevatedButton.styleFrom(shape: const CircleBorder()), - child: Icon(iconData), + child: Icon( + iconData, + color: color, + ), ); } diff --git a/packages/smooth_app/lib/pages/crop_parameters.dart b/packages/smooth_app/lib/pages/crop_parameters.dart new file mode 100644 index 00000000000..f2f6d52c35a --- /dev/null +++ b/packages/smooth_app/lib/pages/crop_parameters.dart @@ -0,0 +1,29 @@ +import 'dart:io'; + +/// Parameters of the crop operation. +class CropParameters { + const CropParameters({ + this.fullFile, + required this.smallCroppedFile, + required this.rotation, + required this.x1, + required this.y1, + required this.x2, + required this.y2, + this.eraserCoordinates, + }); + + /// File of the full image. + final File? fullFile; + + /// File of the cropped image, resized according to the screen. + final File smallCroppedFile; + + final int rotation; + final int x1; + final int y1; + final int x2; + final int y2; + + final List? eraserCoordinates; +} diff --git a/packages/smooth_app/lib/pages/guides/guide/guide_nutriscore_v2.dart b/packages/smooth_app/lib/pages/guides/guide/guide_nutriscore_v2.dart new file mode 100644 index 00000000000..098f2a70d34 --- /dev/null +++ b/packages/smooth_app/lib/pages/guides/guide/guide_nutriscore_v2.dart @@ -0,0 +1,213 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:smooth_app/pages/guides/helpers/guides_content.dart'; +import 'package:smooth_app/pages/guides/helpers/guides_footer.dart'; +import 'package:smooth_app/pages/guides/helpers/guides_header.dart'; +import 'package:smooth_app/resources/app_icons.dart'; + +class GuideNutriscoreV2 extends StatelessWidget { + const GuideNutriscoreV2({super.key}); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return GuidesPage( + pageName: 'NutriscoreV2', + header: const _NutriscoreHeader(), + body: const [ + _NutriScoreSection1(), + _NutriScoreSection2(), + _NutriScoreSection3(), + _NutriScoreSection4(), + _NutriScoreSection5(), + ], + footer: SliverToBoxAdapter( + child: GuidesFooter( + shareMessage: appLocalizations.guide_nutriscore_v2_share_message, + shareUrl: appLocalizations.guide_nutriscore_v2_share_link, + ), + ), + ); + } +} + +class _NutriscoreHeader extends StatelessWidget { + const _NutriscoreHeader(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return GuidesHeader( + title: appLocalizations.guide_nutriscore_v2_title, + illustration: const _NutriScoreHeaderIllustration(), + ); + } +} + +class _NutriScoreHeaderIllustration extends StatelessWidget { + const _NutriScoreHeaderIllustration(); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Expanded( + flex: 32, + child: SvgPicture.asset('assets/cache/nutriscore-a.svg'), + ), + const Expanded( + flex: 28, + child: Arrow.down( + color: Colors.white, + ), + ), + Expanded( + flex: 40, + child: SvgPicture.asset( + 'assets/cache/nutriscore-a-new-${AppLocalizations.of(context).guide_nutriscore_v2_file_language}.svg'), + ), + ], + ); + } +} + +class _NutriScoreSection1 extends StatelessWidget { + const _NutriScoreSection1(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return GuidesParagraph( + title: appLocalizations.guide_nutriscore_v2_what_is_nutriscore_title, + content: [ + GuidesText( + text: appLocalizations + .guide_nutriscore_v2_what_is_nutriscore_paragraph1, + ), + GuidesText( + text: appLocalizations + .guide_nutriscore_v2_what_is_nutriscore_paragraph2, + ), + GuidesImage( + imagePath: 'assets/cache/nutriscore-a.svg', + caption: appLocalizations.guide_nutriscore_v2_nutriscore_a_caption, + desiredWidthPercent: 0.30, + ), + ], + ); + } +} + +class _NutriScoreSection2 extends StatelessWidget { + const _NutriScoreSection2(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return GuidesParagraph( + title: appLocalizations.guide_nutriscore_v2_why_v2_title, + content: [ + GuidesText( + text: appLocalizations.guide_nutriscore_v2_why_v2_intro, + ), + GuidesTitleWithText( + title: appLocalizations.guide_nutriscore_v2_why_v2_arg1_title, + icon: const Milk(), + text: appLocalizations.guide_nutriscore_v2_why_v2_arg1_text, + ), + GuidesTitleWithText( + title: appLocalizations.guide_nutriscore_v2_why_v2_arg2_title, + icon: const Soda.unhappy(), + text: appLocalizations.guide_nutriscore_v2_why_v2_arg2_text, + ), + GuidesTitleWithText( + title: appLocalizations.guide_nutriscore_v2_why_v2_arg3_title, + icon: const Salt(), + text: appLocalizations.guide_nutriscore_v2_why_v2_arg3_text, + ), + GuidesTitleWithText( + title: appLocalizations.guide_nutriscore_v2_why_v2_arg4_title, + icon: const Fish(), + text: appLocalizations.guide_nutriscore_v2_why_v2_arg4_text, + ), + GuidesTitleWithText( + title: appLocalizations.guide_nutriscore_v2_why_v2_arg5_title, + icon: const Chicken(), + text: appLocalizations.guide_nutriscore_v2_why_v2_arg5_text, + ), + ], + ); + } +} + +class _NutriScoreSection3 extends StatelessWidget { + const _NutriScoreSection3(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return GuidesParagraph( + title: appLocalizations.guide_nutriscore_v2_new_logo_title, + content: [ + GuidesText( + text: appLocalizations.guide_nutriscore_v2_new_logo_text, + ), + GuidesImage( + imagePath: + 'assets/cache/nutriscore-a-new-${AppLocalizations.of(context).guide_nutriscore_v2_file_language}.svg', + caption: appLocalizations.guide_nutriscore_v2_new_logo_image_caption, + desiredWidthPercent: 0.30, + ), + ], + ); + } +} + +class _NutriScoreSection4 extends StatelessWidget { + const _NutriScoreSection4(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return GuidesParagraph( + title: appLocalizations.guide_nutriscore_v2_where_title, + content: [ + GuidesText(text: appLocalizations.guide_nutriscore_v2_where_paragraph1), + GuidesText(text: appLocalizations.guide_nutriscore_v2_where_paragraph2), + GuidesIllustratedText( + text: appLocalizations.guide_nutriscore_v2_where_paragraph3, + imagePath: 'assets/app/release_icon_light_transparent_no_border.svg', + desiredWidthPercent: 0.15, + ) + ], + ); + } +} + +class _NutriScoreSection5 extends StatelessWidget { + const _NutriScoreSection5(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return GuidesParagraph( + title: appLocalizations.guide_nutriscore_v2_unchanged_title, + content: [ + GuidesText( + text: appLocalizations.guide_nutriscore_v2_unchanged_paragraph1, + ), + GuidesText( + text: appLocalizations.guide_nutriscore_v2_unchanged_paragraph2, + ), + ], + ); + } +} diff --git a/packages/smooth_app/lib/pages/guides/helpers/guides_content.dart b/packages/smooth_app/lib/pages/guides/helpers/guides_content.dart new file mode 100644 index 00000000000..983f588dc27 --- /dev/null +++ b/packages/smooth_app/lib/pages/guides/helpers/guides_content.dart @@ -0,0 +1,538 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:provider/provider.dart'; +import 'package:sliver_tools/sliver_tools.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/helpers/physics.dart'; +import 'package:smooth_app/helpers/strings_helper.dart'; +import 'package:smooth_app/pages/guides/helpers/guides_header.dart'; +import 'package:smooth_app/resources/app_icons.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; + +class GuidesPage extends StatelessWidget { + const GuidesPage({ + required this.header, + required this.body, + required this.pageName, + this.footer, + super.key, + }); + + final Widget header; + final List body; + final Widget? footer; + + // Page name for the Analytics event + final String pageName; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: _GuidesPageBody( + pageName: pageName, + slivers: [ + header, + ...body, + if (footer != null) footer!, + ], + ), + ); + } +} + +class _GuidesPageBody extends StatefulWidget { + const _GuidesPageBody({ + required this.slivers, + required this.pageName, + }) : assert(pageName.length > 0); + + final List slivers; + final String pageName; + + @override + State<_GuidesPageBody> createState() => _GuidesPageBodyState(); +} + +class _GuidesPageBodyState extends State<_GuidesPageBody> + with TraceableClientMixin { + final ScrollController _controller = ScrollController(); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (_) => _controller, + child: NotificationListener( + onNotification: (ScrollNotification notification) { + /// Snap to positions when the user stops scrolling. + if (notification is ScrollEndNotification) { + if (notification.dragDetails == null) { + return true; + } + + if (notification.metrics.pixels < 125) { + Future.delayed(Duration.zero, () { + _controller.animateTo(0, + duration: const Duration(milliseconds: 500), + curve: Curves.ease); + }); + return true; + } else if (notification.metrics.pixels < 250) { + Future.delayed(Duration.zero, () { + _controller.animateTo(250 - kToolbarHeight, + duration: const Duration(milliseconds: 500), + curve: Curves.ease); + }); + } + return true; + } + return false; + }, + child: CustomScrollView( + controller: _controller, + physics: VerticalSnapScrollPhysics.get( + lastStepBlocking: false, + steps: const [ + 0, + GuidesHeader.HEADER_HEIGHT - kToolbarHeight, + ], + ), + slivers: widget.slivers, + ), + ), + ); + } + + @override + String get actionName => 'Opened ${widget.pageName}'; +} + +class GuidesParagraph extends StatelessWidget { + const GuidesParagraph({ + super.key, + required this.title, + required this.content, + }); + + static const double _HORIZONTAL_PADDING = 20.0; + + final String title; + final List content; + + @override + Widget build(BuildContext context) { + return MultiSliver( + pushPinnedChildren: true, + children: [ + SliverPadding( + padding: const EdgeInsetsDirectional.only( + bottom: 8.0, + ), + sliver: SliverPinnedHeader( + child: _GuidesParagraphTitle(title: title), + ), + ), + DefaultTextStyle.merge( + style: const TextStyle( + fontSize: 15.0, + height: 1.75, + ), + child: AppIconTheme( + size: 21.0, + color: Colors.white, + child: SliverPadding( + padding: const EdgeInsetsDirectional.only( + bottom: 15.0, + ), + sliver: SliverList.builder( + itemCount: content.length, + itemBuilder: (BuildContext context, int position) { + return content[position]; + }, + ), + ), + ), + ), + ], + ); + } +} + +class _GuidesParagraphTitle extends StatelessWidget { + const _GuidesParagraphTitle({ + required this.title, + }) : assert(title.length > 0); + + final String title; + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + + return Semantics( + label: title, + header: true, + excludeSemantics: true, + child: ColoredBox( + color: colors.primaryDark, + child: Padding( + padding: const EdgeInsetsDirectional.symmetric( + horizontal: GuidesParagraph._HORIZONTAL_PADDING, + vertical: 10.0, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.ideographic, + children: [ + const Padding( + padding: EdgeInsetsDirectional.only(top: 3.3), + child: _GuidesParagraphArrow(), + ), + const SizedBox(width: 10.0), + Expanded( + child: Text( + title, + style: const TextStyle( + color: Colors.white, + fontSize: 17.0, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ), + ), + ); + } +} + +class _GuidesParagraphArrow extends StatelessWidget { + const _GuidesParagraphArrow(); + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + + return SizedBox.square( + dimension: 20.0, + child: DecoratedBox( + decoration: BoxDecoration( + color: colors.orange, + shape: BoxShape.circle, + ), + child: const Arrow.right( + color: Colors.white, + size: 12.0, + ), + ), + ); + } +} + +class GuidesText extends StatelessWidget { + const GuidesText({ + required this.text, + super.key, + }); + + final String text; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.only( + top: 10.0, + start: GuidesParagraph._HORIZONTAL_PADDING, + end: GuidesParagraph._HORIZONTAL_PADDING, + ), + child: _GuidesFormattedText( + text: text, + ), + ); + } +} + +class _GuidesFormattedText extends StatelessWidget { + const _GuidesFormattedText({ + required this.text, + }); + + final String text; + + @override + Widget build(BuildContext context) { + return FormattedText(text: text); + } +} + +class GuidesIllustratedText extends StatelessWidget { + const GuidesIllustratedText({ + required this.text, + required this.imagePath, + required this.desiredWidthPercent, + super.key, + }) : assert(text.length > 0), + assert(imagePath.length > 0); + + final String text; + final String imagePath; + final double? desiredWidthPercent; + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + final int imageWidth = + (desiredWidthPercent != null ? desiredWidthPercent! : 0.25) * + 100.0 ~/ + 1; + + return Semantics( + label: text, + excludeSemantics: true, + child: Padding( + padding: const EdgeInsetsDirectional.only( + top: VERY_LARGE_SPACE, + ), + child: ColoredBox( + color: colors.primaryMedium, + child: Padding( + padding: const EdgeInsetsDirectional.symmetric( + vertical: 15.0, + horizontal: GuidesParagraph._HORIZONTAL_PADDING, + ), + child: Row( + children: [ + Expanded( + flex: imageWidth, + child: _ImageFromAssets( + imagePath: imagePath, + ), + ), + const SizedBox(width: 15.0), + Expanded( + flex: 100 - imageWidth, + child: DefaultTextStyle.merge( + style: const TextStyle(color: Colors.black), + child: _GuidesFormattedText(text: text), + ), + ), + ], + ), + ), + ), + ), + ); + } +} + +class GuidesTitleWithText extends StatelessWidget { + const GuidesTitleWithText({ + required this.title, + required this.icon, + required this.text, + super.key, + }); + + final String title; + final AppIcon icon; + final String text; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.only( + top: 15.0, + bottom: 15.0, + start: GuidesParagraph._HORIZONTAL_PADDING - 2.0, + end: GuidesParagraph._HORIZONTAL_PADDING - 2.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _GuidesTextTitle( + title: title, + icon: icon, + ), + const SizedBox(height: 15.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 2.0), + child: _GuidesFormattedText( + text: text, + ), + ), + ], + ), + ); + } +} + +class _GuidesTextTitle extends StatelessWidget { + const _GuidesTextTitle({ + required this.title, + required this.icon, + }) : assert(title.length > 0); + + final String title; + final AppIcon icon; + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + + return DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + color: colors.orange, + ), + child: Row( + children: [ + const SizedBox(width: 14.0), + icon, + const SizedBox(width: 11.0), + Expanded( + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + color: colors.primarySemiDark, + ), + child: Padding( + padding: const EdgeInsetsDirectional.only( + start: 14.0, + end: 14.0, + top: 5.0, + bottom: 6.0, + ), + child: Text( + title, + style: const TextStyle( + fontSize: 15.5, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ), + ], + ), + ); + } +} + +class GuidesImage extends StatelessWidget { + const GuidesImage({ + required this.imagePath, + required this.caption, + this.desiredWidthPercent, + this.desiredHeightPercent, + super.key, + }) : assert(caption.length > 0), + assert(desiredWidthPercent == null || + desiredWidthPercent >= 0.0 && desiredWidthPercent <= 1.0), + assert(desiredHeightPercent == null || + desiredHeightPercent >= 0.0 && desiredHeightPercent <= 1.0); + + final String imagePath; + final double? desiredWidthPercent; + final double? desiredHeightPercent; + final String caption; + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + + return Semantics( + label: caption, + image: true, + excludeSemantics: true, + child: Padding( + padding: const EdgeInsetsDirectional.only( + top: 10.0, + start: GuidesParagraph._HORIZONTAL_PADDING, + end: GuidesParagraph._HORIZONTAL_PADDING, + ), + child: DecoratedBox( + decoration: BoxDecoration( + color: colors.primaryMedium, + borderRadius: BorderRadius.circular(20.0)), + child: Padding( + padding: const EdgeInsetsDirectional.only( + top: 14.0, + bottom: SMALL_SPACE, + start: MEDIUM_SPACE, + end: MEDIUM_SPACE, + ), + child: Column( + children: [ + _ImageFromAssets( + imagePath: imagePath, + desiredWidthPercent: desiredWidthPercent, + desiredHeightPercent: desiredHeightPercent, + ), + const SizedBox(height: 5.0), + Text( + caption, + style: const TextStyle( + fontSize: 13.0, + fontStyle: FontStyle.italic, + color: Colors.black, + ), + ), + ], + ), + ), + ), + ), + ); + } +} + +class _ImageFromAssets extends StatelessWidget { + const _ImageFromAssets({ + required this.imagePath, + this.desiredWidthPercent, + this.desiredHeightPercent, + }) : assert(desiredWidthPercent == null || + desiredWidthPercent >= 0.0 && desiredWidthPercent <= 1.0), + assert(desiredHeightPercent == null || + desiredHeightPercent >= 0.0 && desiredHeightPercent <= 1.0); + + final String imagePath; + final double? desiredWidthPercent; + final double? desiredHeightPercent; + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (_, BoxConstraints constraints) { + if (imagePath.endsWith('.svg')) { + return SvgPicture.asset( + imagePath, + width: desiredWidthPercent != null + ? constraints.maxWidth * desiredWidthPercent! + : null, + height: desiredHeightPercent != null + ? constraints.maxHeight * desiredHeightPercent! + : null, + ); + } else { + return Image.asset( + imagePath, + width: desiredWidthPercent != null + ? constraints.maxWidth * desiredWidthPercent! + : null, + height: desiredHeightPercent != null + ? constraints.maxHeight * desiredHeightPercent! + : null, + ); + } + }, + ); + } +} diff --git a/packages/smooth_app/lib/pages/guides/helpers/guides_footer.dart b/packages/smooth_app/lib/pages/guides/helpers/guides_footer.dart new file mode 100644 index 00000000000..1ff923eba8d --- /dev/null +++ b/packages/smooth_app/lib/pages/guides/helpers/guides_footer.dart @@ -0,0 +1,131 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/helpers/analytics_helper.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; + +class GuidesFooter extends StatelessWidget { + const GuidesFooter({ + required this.shareUrl, + this.shareMessage, + super.key, + }); + + final String? shareMessage; + final String shareUrl; + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + + return CustomPaint( + painter: _FooterPainter( + color: colors.primaryNormal, + wazeSize: _FooterPainter.WAVE_SIZE, + ), + child: SizedBox( + width: double.infinity, + child: Padding( + padding: EdgeInsetsDirectional.only( + top: _FooterPainter.WAVE_SIZE + MEDIUM_SPACE, + start: VERY_LARGE_SPACE, + end: VERY_LARGE_SPACE, + bottom: 10.0 + MediaQuery.viewPaddingOf(context).bottom, + ), + child: TextButton( + style: TextButton.styleFrom( + foregroundColor: colors.primaryBlack, + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24.0), + ), + padding: const EdgeInsetsDirectional.symmetric( + horizontal: VERY_LARGE_SPACE / 2, + vertical: VERY_LARGE_SPACE, + ), + ), + child: Text( + AppLocalizations.of(context).guide_share_label, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15.5, + ), + ), + onPressed: () { + Share.share(_shareText); + AnalyticsHelper.trackOutlink(url: shareUrl); + }, + ), + ), + ), + ); + } + + String get _shareText { + if (shareMessage?.isNotEmpty == true) { + return '$shareMessage\n$shareUrl'; + } else { + return shareUrl; + } + } +} + +class _FooterPainter extends CustomPainter { + _FooterPainter({ + this.wazeSize = WAVE_SIZE, + required Color color, + }) : assert(color.opacity > 0.0), + _localPaint = Paint() + ..color = color + ..style = PaintingStyle.fill; + + static const double WAVE_SIZE = 24.0; + final double wazeSize; + final Paint _localPaint; + + @override + void paint(Canvas canvas, Size size) { + Offset offset = Offset(wazeSize / 2, wazeSize / 2); + + /// Draw top waves + while (true) { + canvas.drawArc( + Rect.fromCenter( + center: offset, + height: wazeSize, + width: wazeSize, + ), + math.pi, + math.pi, + false, + _localPaint, + ); + + offset = offset.translate(wazeSize, 0); + if (offset.dx > (size.width + wazeSize)) { + break; + } + } + + /// Draw background color + canvas.drawRect( + Rect.fromLTWH( + 0, + // 0.5 to eliminate some glitches + (wazeSize / 2) - 0.5, + size.width, + size.height, + ), + _localPaint); + } + + @override + bool shouldRepaint(_FooterPainter oldDelegate) => false; + + @override + bool shouldRebuildSemantics(_FooterPainter oldDelegate) => false; +} diff --git a/packages/smooth_app/lib/pages/guides/helpers/guides_header.dart b/packages/smooth_app/lib/pages/guides/helpers/guides_header.dart new file mode 100644 index 00000000000..9ac100a7827 --- /dev/null +++ b/packages/smooth_app/lib/pages/guides/helpers/guides_header.dart @@ -0,0 +1,444 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/helpers/num_utils.dart'; +import 'package:smooth_app/resources/app_icons.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; + +/// A collapsing header with: +/// In the expanded state: +/// - A close button with the "Guide" text +/// - A title on multiple lines +/// - An illustration +/// In the minimized state: +/// - A close button (just an X) +/// - A title on a single line +class GuidesHeader extends StatelessWidget { + const GuidesHeader({ + required this.title, + required this.illustration, + super.key, + }); + + static const double HEADER_HEIGHT = 250.0; + + final String title; + final Widget illustration; + + @override + Widget build(BuildContext context) { + return DefaultTextStyle.merge( + style: const TextStyle(color: Colors.white), + child: SliverPadding( + padding: const EdgeInsetsDirectional.only( + bottom: 10.0, + ), + // Pinned = for the header to stay at the top of the screen + sliver: SliverPersistentHeader( + floating: false, + pinned: true, + delegate: _GuidesHeaderDelegate( + title: title, + illustration: illustration, + topPadding: MediaQuery.viewPaddingOf(context).top, + ), + ), + ), + ); + } +} + +class _GuidesHeaderDelegate extends SliverPersistentHeaderDelegate { + const _GuidesHeaderDelegate({ + required this.title, + required this.illustration, + required this.topPadding, + }) : assert(title.length > 0), + assert(topPadding >= 0.0); + + final String title; + final Widget illustration; + final double topPadding; + + @override + Widget build( + BuildContext context, + double shrinkOffset, + bool overlapsContent, + ) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + final double progress = + shrinkOffset.progressAndClamp(0.0, maxExtent - minExtent, 1.0); + + return Provider.value( + value: progress, + child: Container( + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(30.0 * (1 - progress)), + ), + ), + color: colors.primaryDark, + shadows: [ + BoxShadow( + color: Colors.black + .withOpacity(progress.progressAndClamp(0.5, 1, 0.2)), + offset: const Offset(0.5, 0.5), + blurRadius: 2.0, + ), + ], + ), + padding: const EdgeInsetsDirectional.symmetric( + horizontal: VERY_LARGE_SPACE, + ), + child: ClipRRect( + child: CustomMultiChildLayout( + delegate: _GuidesHeaderLayout( + topPadding: topPadding, + ), + children: [ + LayoutId( + id: _GuidesHeaderLayoutId.expandedTitle, + child: Opacity( + opacity: 1 - progress, + child: OverflowBox( + fit: OverflowBoxFit.deferToChild, + maxHeight: GuidesHeader.HEADER_HEIGHT - + 10 - + _CloseButtonLayout._CLOSE_BUTTON_SIZE, + child: Align( + alignment: Alignment.bottomLeft, + child: Padding( + padding: const EdgeInsets.only(bottom: 10.0), + child: AutoSizeText( + title, + maxLines: 4, + textAlign: TextAlign.start, + style: const TextStyle( + fontSize: 24.0, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + ), + ), + ), + ), + ), + ), + ), + LayoutId( + id: _GuidesHeaderLayoutId.illustration, + child: OverflowBox( + maxHeight: GuidesHeader.HEADER_HEIGHT - 33, + fit: OverflowBoxFit.deferToChild, + child: Offstage( + offstage: progress == 1.0, + child: Opacity( + opacity: 1 - progress, + child: illustration, + ), + ), + ), + ), + LayoutId( + id: _GuidesHeaderLayoutId.minimizedTitle, + child: Offstage( + offstage: progress < 0.95, + child: Opacity( + opacity: progress.progressAndClamp(0.95, 1.0, 1.0), + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + LayoutId( + id: _GuidesHeaderLayoutId.closeButton, + child: const _BackButton(), + ), + ], + ), + ), + ), + ); + } + + @override + double get maxExtent => GuidesHeader.HEADER_HEIGHT + topPadding; + + @override + double get minExtent => kToolbarHeight + topPadding; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { + return true; + } +} + +class _GuidesHeaderLayout extends MultiChildLayoutDelegate { + _GuidesHeaderLayout({ + required this.topPadding, + }); + + final double topPadding; + + @override + void performLayout(Size size) { + final double topMargin = topPadding + 10.0; + final double maxHeight = size.height - topPadding - (10.0 * 2); + + final Size closeButtonSize = layoutChild( + _GuidesHeaderLayoutId.closeButton, + BoxConstraints.loose( + Size( + size.width * 0.6, + _CloseButtonLayout._CLOSE_BUTTON_SIZE, + ), + ), + ); + + layoutChild( + _GuidesHeaderLayoutId.expandedTitle, + BoxConstraints.loose( + Size( + size.width * 0.6, + maxHeight - closeButtonSize.height, + ), + ), + ); + + final Size illustrationSize = layoutChild( + _GuidesHeaderLayoutId.illustration, + BoxConstraints.loose( + Size( + size.width * 0.4, + maxHeight, + ), + ), + ); + + layoutChild( + _GuidesHeaderLayoutId.minimizedTitle, + BoxConstraints.loose( + Size( + size.width - _CloseButtonLayout._CLOSE_BUTTON_SIZE, + _CloseButtonLayout._CLOSE_BUTTON_SIZE, + ), + ), + ); + + positionChild(_GuidesHeaderLayoutId.closeButton, Offset(0, topMargin)); + positionChild( + _GuidesHeaderLayoutId.expandedTitle, + Offset(0, closeButtonSize.height + topPadding), + ); + positionChild( + _GuidesHeaderLayoutId.illustration, + Offset(size.width * 0.6, + topPadding + (maxHeight - illustrationSize.height) + 5.0), + ); + + positionChild( + _GuidesHeaderLayoutId.minimizedTitle, + Offset( + _CloseButtonLayout._CLOSE_BUTTON_SIZE + 10.0, + topMargin + 5.0, + ), + ); + } + + @override + bool shouldRelayout(_GuidesHeaderLayout oldDelegate) { + return oldDelegate.topPadding != topPadding; + } +} + +enum _GuidesHeaderLayoutId { + closeButton, + expandedTitle, + minimizedTitle, + illustration, +} + +class _BackButton extends StatelessWidget { + const _BackButton(); + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + + return SizedBox( + height: _CloseButtonLayout._CLOSE_BUTTON_SIZE, + child: Material( + type: MaterialType.transparency, + child: Consumer( + builder: (_, double progress, __) { + return CustomMultiChildLayout( + delegate: _CloseButtonLayout( + progress: 1 - progress, + ), + children: [ + LayoutId( + id: _CloseButtonLayoutId.text, + child: Offstage( + offstage: progress == 1.0, + child: ExcludeSemantics( + child: Padding( + padding: const EdgeInsetsDirectional.only( + start: 10.0, + end: 24.0, + ), + child: Opacity( + opacity: 1 - progress.progressAndClamp(0.0, 0.7, 1.0), + child: const Text( + 'Guide', + style: TextStyle( + fontSize: 18.0, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ), + ), + ), + LayoutId( + id: _CloseButtonLayoutId.closeButton, + child: DecoratedBox( + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: SizedBox.square( + dimension: 36.0, + child: Close( + size: 16.0, + color: colors.primaryBlack, + ), + ), + ), + ), + LayoutId( + id: _CloseButtonLayoutId.background, + child: Tooltip( + message: + MaterialLocalizations.of(context).closeButtonTooltip, + child: InkWell( + onTap: () => Navigator.of(context).maybePop(true), + borderRadius: ROUNDED_BORDER_RADIUS, + child: Offstage( + offstage: progress == 1.0, + child: Container( + decoration: const ShapeDecoration( + shape: RoundedRectangleBorder( + side: BorderSide( + color: Colors.white, + width: 1.0, + ), + borderRadius: ROUNDED_BORDER_RADIUS, + ), + ), + ), + ), + ), + ), + ), + ], + ); + }, + ), + ), + ); + } +} + +class _CloseButtonLayout extends MultiChildLayoutDelegate { + _CloseButtonLayout({required this.progress}) + : assert(progress >= 0.0 && progress <= 1.0); + + static const double _CLOSE_BUTTON_SIZE = 36.0; + + final double progress; + + @override + void performLayout(Size size) { + final Size closeButtonSize = layoutChild( + _CloseButtonLayoutId.closeButton, + const BoxConstraints.expand( + width: _CLOSE_BUTTON_SIZE, + height: _CLOSE_BUTTON_SIZE, + ), + ); + + if (progress == 0.0) { + layoutChild( + _CloseButtonLayoutId.text, + BoxConstraints.loose(Size.zero), + ); + + layoutChild( + _CloseButtonLayoutId.background, + BoxConstraints.expand( + width: closeButtonSize.width, + height: closeButtonSize.height, + ), + ); + + return; + } + + final Size textSize = layoutChild( + _CloseButtonLayoutId.text, + BoxConstraints.loose(size), + ); + + layoutChild( + _CloseButtonLayoutId.background, + BoxConstraints.expand( + width: closeButtonSize.width + (textSize.width * progress), + height: closeButtonSize.height, + ), + ); + + positionChild(_CloseButtonLayoutId.closeButton, Offset.zero); + positionChild( + _CloseButtonLayoutId.text, + Offset( + _CLOSE_BUTTON_SIZE - ((textSize.width - 24.0) * (1 - progress)), + ((_CLOSE_BUTTON_SIZE - textSize.height) / 2) - 1, + ), + ); + positionChild(_CloseButtonLayoutId.background, Offset.zero); + } + + @override + Size getSize(BoxConstraints constraints) { + if (progress == 0.0) { + return const Size.square(_CLOSE_BUTTON_SIZE); + } else { + return Size(constraints.biggest.width, _CLOSE_BUTTON_SIZE); + } + } + + @override + bool shouldRelayout(_CloseButtonLayout oldDelegate) { + return oldDelegate.progress != progress; + } +} + +enum _CloseButtonLayoutId { + closeButton, + text, + background, +} diff --git a/packages/smooth_app/lib/pages/hunger_games/congrats.dart b/packages/smooth_app/lib/pages/hunger_games/congrats.dart index 5d6ea5e5bfb..7ade32b72fc 100644 --- a/packages/smooth_app/lib/pages/hunger_games/congrats.dart +++ b/packages/smooth_app/lib/pages/hunger_games/congrats.dart @@ -167,7 +167,7 @@ class _Header extends StatelessWidget { Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); final double multiplier = - math.min(350, MediaQuery.of(context).size.height * 0.3) / 235; + math.min(350, MediaQuery.sizeOf(context).height * 0.3) / 235; return Semantics( enabled: true, diff --git a/packages/smooth_app/lib/pages/hunger_games/question_answers_options.dart b/packages/smooth_app/lib/pages/hunger_games/question_answers_options.dart index 9c1f69d392f..e1ee8172f20 100755 --- a/packages/smooth_app/lib/pages/hunger_games/question_answers_options.dart +++ b/packages/smooth_app/lib/pages/hunger_games/question_answers_options.dart @@ -23,7 +23,7 @@ class QuestionAnswersOptions extends StatelessWidget { @override Widget build(BuildContext context) { - final double yesNoHeight = MediaQuery.of(context).size.width / (3 * 1.25); + final double yesNoHeight = MediaQuery.sizeOf(context).width / (3 * 1.25); return Column( mainAxisSize: MainAxisSize.min, @@ -108,8 +108,8 @@ class QuestionAnswersOptions extends StatelessWidget { child: TextButton.icon( onPressed: () => onAnswer(insightAnnotation), style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(backgroundColor), - shape: MaterialStateProperty.all( + backgroundColor: WidgetStateProperty.all(backgroundColor), + shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: ROUNDED_BORDER_RADIUS, ), diff --git a/packages/smooth_app/lib/pages/hunger_games/question_card.dart b/packages/smooth_app/lib/pages/hunger_games/question_card.dart index 47d8f68045c..2494d5e1ccf 100755 --- a/packages/smooth_app/lib/pages/hunger_games/question_card.dart +++ b/packages/smooth_app/lib/pages/hunger_games/question_card.dart @@ -30,7 +30,7 @@ class QuestionCard extends StatelessWidget { context.read(), ); - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); return FutureBuilder( future: productFuture, diff --git a/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart b/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart index 2b7b787428e..3936ac36b1a 100644 --- a/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart +++ b/packages/smooth_app/lib/pages/image/product_image_gallery_other_view.dart @@ -1,15 +1,14 @@ -import 'package:auto_size_text/auto_size_text.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:intl/intl.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/fetched_product.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; -import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; import 'package:smooth_app/pages/image/product_image_other_page.dart'; +import 'package:smooth_app/pages/image/product_image_widget.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; /// Number of columns for the grid. @@ -107,12 +106,11 @@ class _RawGridGallery extends StatelessWidget { final Product product; final List rawImages; - static final DateFormat _dateFormat = DateFormat('yyyy-MM-dd'); - @override Widget build(BuildContext context) { final double squareSize = _getSquareSize(context); - final DateTime now = DateTime.now(); + final ImageSize? imageSize = _computeImageSize(squareSize); + return SliverGrid( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: _columns, @@ -122,61 +120,29 @@ class _RawGridGallery extends StatelessWidget { // order by descending ids index = rawImages.length - 1 - index; final ProductImage productImage = rawImages[index]; - final DateTime? uploaded = productImage.uploaded; - final String? date; - final bool expired; - if (uploaded == null) { - date = null; - expired = false; - } else { - date = _dateFormat.format(uploaded); - expired = now.difference(uploaded).inDays > 365; - } - final Widget image = SmoothImage( - width: squareSize, - height: squareSize, - imageProvider: NetworkImage(productImage.getUrl(product.barcode!)), - rounded: false, - ); - return InkWell( - onTap: () async => Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => ProductImageOtherPage( - product, - int.parse(productImage.imgid!), + return Padding( + padding: EdgeInsetsDirectional.only( + start: VERY_SMALL_SPACE, + end: index % _columns == 0 ? VERY_SMALL_SPACE : 0.0, + bottom: VERY_SMALL_SPACE, + ), + child: InkWell( + onTap: () async => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => ProductImageOtherPage( + product, + int.parse(productImage.imgid!), + ), ), ), + child: ProductImageWidget( + productImage: productImage, + barcode: product.barcode!, + squareSize: squareSize, + imageSize: imageSize, + ), ), - child: date == null - ? image - : Stack( - children: [ - image, - SizedBox( - width: squareSize, - height: squareSize, - child: Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.all(SMALL_SPACE), - child: Container( - height: VERY_LARGE_SPACE, - color: expired - ? Colors.red.withAlpha(128) - : Colors.white.withAlpha(128), - child: Center( - child: AutoSizeText( - date, - maxLines: 1, - ), - ), - ), - ), - ), - ), - ], - ), ); }, addAutomaticKeepAlives: false, @@ -184,4 +150,12 @@ class _RawGridGallery extends StatelessWidget { ), ); } + + ImageSize? _computeImageSize(double squareSize) => [ + ImageSize.THUMB, + ImageSize.SMALL, + ImageSize.DISPLAY + ].firstWhereOrNull( + (ImageSize element) => squareSize <= int.parse(element.number), + ); } diff --git a/packages/smooth_app/lib/pages/image/product_image_other_page.dart b/packages/smooth_app/lib/pages/image/product_image_other_page.dart index 8557c5fc9ce..d8722384468 100644 --- a/packages/smooth_app/lib/pages/image/product_image_other_page.dart +++ b/packages/smooth_app/lib/pages/image/product_image_other_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/query/product_query.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; /// Full page display of a raw product image. @@ -28,7 +29,10 @@ class ProductImageOtherPage extends StatelessWidget { ProductImage.raw( imgid: imageId.toString(), size: ImageSize.ORIGINAL, - ).getUrl(product.barcode!), + ).getUrl( + product.barcode!, + uriHelper: ProductQuery.uriProductHelper, + ), ), fit: BoxFit.cover, ), diff --git a/packages/smooth_app/lib/pages/image/product_image_widget.dart b/packages/smooth_app/lib/pages/image/product_image_widget.dart new file mode 100644 index 00000000000..50dd84ded55 --- /dev/null +++ b/packages/smooth_app/lib/pages/image/product_image_widget.dart @@ -0,0 +1,112 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:intl/intl.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/query/product_query.dart'; +import 'package:smooth_app/resources/app_icons.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; + +/// Displays a product image thumbnail with the upload date on top. +class ProductImageWidget extends StatelessWidget { + const ProductImageWidget({ + required this.productImage, + required this.barcode, + required this.squareSize, + this.imageSize, + }); + + final ProductImage productImage; + final String barcode; + final double squareSize; + + /// Allows to fetch the optimized version of the image + final ImageSize? imageSize; + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final DateFormat dateFormat = + DateFormat.yMd(ProductQuery.getLanguage().offTag); + + final Widget image = SmoothImage( + cacheHeight: + (squareSize * MediaQuery.devicePixelRatioOf(context)).toInt(), + width: squareSize, + height: squareSize, + imageProvider: NetworkImage( + productImage.getUrl( + barcode, + uriHelper: ProductQuery.uriProductHelper, + imageSize: imageSize, + ), + ), + rounded: false, + ); + final DateTime? uploaded = productImage.uploaded; + if (uploaded == null) { + return image; + } + final bool expired = DateTime.now().difference(uploaded).inDays > 365; + final String date = dateFormat.format(uploaded); + + return Semantics( + label: expired + ? appLocalizations.product_image_outdated_accessibility_label(date) + : appLocalizations.product_image_accessibility_label(date), + excludeSemantics: true, + button: true, + child: SmoothCard( + padding: EdgeInsets.zero, + color: colors.primaryBlack, + borderRadius: ANGULAR_BORDER_RADIUS, + margin: EdgeInsets.zero, + child: ClipRRect( + borderRadius: ANGULAR_BORDER_RADIUS, + child: Column( + children: [ + Expanded( + child: image, + ), + SizedBox( + width: double.infinity, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: SMALL_SPACE, + vertical: VERY_SMALL_SPACE, + ), + child: Stack( + children: [ + Center( + child: AutoSizeText( + date, + maxLines: 1, + style: const TextStyle(color: Colors.white), + ), + ), + if (expired) + Positioned.directional( + end: 0.0, + height: 20.0, + textDirection: Directionality.of(context), + child: Outdated( + size: 18.0, + color: colors.red, + ), + ), + ], + ), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/image/uploaded_image_gallery.dart b/packages/smooth_app/lib/pages/image/uploaded_image_gallery.dart index a7e461787f8..4bfdb032e7b 100644 --- a/packages/smooth_app/lib/pages/image/uploaded_image_gallery.dart +++ b/packages/smooth_app/lib/pages/image/uploaded_image_gallery.dart @@ -7,10 +7,13 @@ import 'package:provider/provider.dart'; import 'package:smooth_app/database/dao_int.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; -import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; import 'package:smooth_app/pages/crop_page.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; +import 'package:smooth_app/pages/image/product_image_widget.dart'; import 'package:smooth_app/pages/image_crop_page.dart'; +import 'package:smooth_app/pages/product_crop_helper.dart'; +import 'package:smooth_app/query/product_query.dart'; import 'package:smooth_app/widgets/smooth_app_bar.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; @@ -36,7 +39,7 @@ class UploadedImageGallery extends StatelessWidget { Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); final MediaQueryData mediaQueryData = MediaQuery.of(context); - final double columnWidth = mediaQueryData.size.width * .45; + final double columnWidth = mediaQueryData.size.width / 2; return SmoothScaffold( backgroundColor: Colors.black, appBar: SmoothAppBar( @@ -51,19 +54,13 @@ class UploadedImageGallery extends StatelessWidget { body: GridView.builder( itemCount: rawImages.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: mediaQueryData.size.width / 2, + maxCrossAxisExtent: columnWidth, childAspectRatio: 1, - mainAxisSpacing: MEDIUM_SPACE, - crossAxisSpacing: MEDIUM_SPACE, ), itemBuilder: (final BuildContext context, int index) { // order by descending ids index = rawImages.length - 1 - index; final ProductImage rawImage = rawImages[index]; - final String url = rawImage.getUrl( - barcode, - imageSize: ImageSize.DISPLAY, - ); return GestureDetector( onTap: () async { final LocalDatabase localDatabase = context.read(); @@ -73,43 +70,38 @@ class UploadedImageGallery extends StatelessWidget { rawImage.getUrl( barcode, imageSize: ImageSize.ORIGINAL, + uriHelper: ProductQuery.uriProductHelper, ), DaoInt(localDatabase), ); if (imageFile == null) { return; } - final File? croppedFile = await navigatorState.push( - MaterialPageRoute( + final CropParameters? parameters = + await navigatorState.push( + MaterialPageRoute( builder: (BuildContext context) => CropPage( - barcode: barcode, - imageField: imageField, inputFile: imageFile, - imageId: int.parse(rawImage.imgid!), initiallyDifferent: true, - language: language, isLoggedInMandatory: isLoggedInMandatory, + cropHelper: ProductCropAgainHelper( + barcode: barcode, + imageField: imageField, + imageId: int.parse(rawImage.imgid!), + language: language, + ), ), fullscreenDialog: true, ), ); - if (croppedFile != null) { + if (parameters != null) { navigatorState.pop(); } }, - child: ClipRRect( - borderRadius: ROUNDED_BORDER_RADIUS, - child: Container( - width: columnWidth, - height: columnWidth, - color: Colors.grey[900], - child: SmoothImage( - width: columnWidth, - height: columnWidth, - imageProvider: NetworkImage(url), - fit: BoxFit.contain, - ), - ), + child: ProductImageWidget( + productImage: rawImage, + barcode: barcode, + squareSize: columnWidth, ), ); }, diff --git a/packages/smooth_app/lib/pages/image_crop_page.dart b/packages/smooth_app/lib/pages/image_crop_page.dart index 62faaa76059..67f902807c1 100644 --- a/packages/smooth_app/lib/pages/image_crop_page.dart +++ b/packages/smooth_app/lib/pages/image_crop_page.dart @@ -18,34 +18,60 @@ import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; import 'package:smooth_app/generic_lib/loading_dialog.dart'; import 'package:smooth_app/helpers/camera_helper.dart'; import 'package:smooth_app/helpers/database_helper.dart'; +import 'package:smooth_app/pages/crop_helper.dart'; import 'package:smooth_app/pages/crop_page.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; +import 'package:smooth_app/pages/product_crop_helper.dart'; -/// Picks an image file from gallery or camera. -Future pickImageFile( - final BuildContext context, { - bool ignorePlatformException = false, -}) async { - final UserPictureSource? source = await _getUserPictureSource(context); - if (source == null) { - return null; - } - final ImagePicker picker = ImagePicker(); - if (source == UserPictureSource.GALLERY) { - try { - return picker.pickImage(source: ImageSource.gallery); - } on PlatformException catch (e) { - // On debug builds this catch won't work. - // Please run on profile/release modes to test it - if (ignorePlatformException) { - return null; - } else if (e.code == 'photo_access_denied') { - throw PhotoAccessDenied(); - } else { - rethrow; +/// Safely picks an image file from gallery or camera, regarding access denied. +Future pickImageFile(final BuildContext context) async { + /// Picks an image file from gallery or camera. + Future innerPickImageFile( + final BuildContext context, { + bool ignorePlatformException = false, + }) async { + final UserPictureSource? source = await _getUserPictureSource(context); + if (source == null) { + return null; + } + final ImagePicker picker = ImagePicker(); + if (source == UserPictureSource.GALLERY) { + try { + return picker.pickImage(source: ImageSource.gallery); + } on PlatformException catch (e) { + // On debug builds this catch won't work. + // Please run on profile/release modes to test it + if (ignorePlatformException) { + return null; + } else if (e.code == 'photo_access_denied') { + throw PhotoAccessDenied(); + } else { + rethrow; + } } } + return picker.pickImage(source: ImageSource.camera); + } + + try { + return innerPickImageFile(context); + } on PhotoAccessDenied catch (_) { + if (!context.mounted) { + return null; + } + final bool? res = await _onGalleryAccessDenied(context); + if (res != true) { + return null; + } + // Let's retry + if (!context.mounted) { + return null; + } + return innerPickImageFile( + context, + ignorePlatformException: true, + ); } - return picker.pickImage(source: ImageSource.camera); } /// Returns the picture source selected by the user. @@ -199,13 +225,13 @@ class _ImageSourceButton extends StatelessWidget { child: OutlinedButton( onPressed: onPressed, style: ButtonStyle( - side: MaterialStatePropertyAll( + side: WidgetStatePropertyAll( BorderSide(color: primaryColor), ), - padding: const MaterialStatePropertyAll( + padding: const WidgetStatePropertyAll( EdgeInsets.symmetric(vertical: LARGE_SPACE), ), - shape: MaterialStatePropertyAll( + shape: WidgetStatePropertyAll( RoundedRectangleBorder( borderRadius: ROUNDED_BORDER_RADIUS, side: BorderSide(color: primaryColor), @@ -226,50 +252,45 @@ class _ImageSourceButton extends StatelessWidget { } } -/// Lets the user pick a picture, crop it, and save it. -Future confirmAndUploadNewPicture( +/// Lets the user pick a new product picture, crop it, and save it. +Future confirmAndUploadNewPicture( final BuildContext context, { required final ImageField imageField, required final String barcode, required final OpenFoodFactsLanguage language, required final bool isLoggedInMandatory, -}) async { - XFile? croppedPhoto; - try { - croppedPhoto = await pickImageFile(context); - } on PhotoAccessDenied catch (_) { - if (!context.mounted) { - return null; - } - final bool? res = await _onGalleryAccessDenied(context); - if (res == true) { - // Let's retry - if (!context.mounted) { - return null; - } - croppedPhoto = await pickImageFile( - context, - ignorePlatformException: true, - ); - } - } +}) async => + confirmAndUploadNewImage( + context, + cropHelper: ProductCropNewHelper( + imageField: imageField, + language: language, + barcode: barcode, + ), + isLoggedInMandatory: isLoggedInMandatory, + ); - if (croppedPhoto == null) { +/// Lets the user pick a picture, crop it, and save it. +Future confirmAndUploadNewImage( + final BuildContext context, { + required final CropHelper cropHelper, + required final bool isLoggedInMandatory, +}) async { + final XFile? fullPhoto = await pickImageFile(context); + if (fullPhoto == null) { return null; } if (!context.mounted) { return null; } - return Navigator.push( + return Navigator.push( context, - MaterialPageRoute( + MaterialPageRoute( builder: (BuildContext context) => CropPage( - barcode: barcode, - imageField: imageField, - inputFile: File(croppedPhoto!.path), + inputFile: File(fullPhoto.path), initiallyDifferent: true, - language: language, isLoggedInMandatory: isLoggedInMandatory, + cropHelper: cropHelper, ), fullscreenDialog: true, ), diff --git a/packages/smooth_app/lib/pages/locations/location_map_page.dart b/packages/smooth_app/lib/pages/locations/location_map_page.dart new file mode 100644 index 00000000000..16e4bdd2845 --- /dev/null +++ b/packages/smooth_app/lib/pages/locations/location_map_page.dart @@ -0,0 +1,122 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:smooth_app/helpers/launch_url_helper.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Page that displays a map centered on a location. +class LocationMapPage extends StatelessWidget { + const LocationMapPage( + this.osmLocation, { + required this.popFirst, + }); + + final OsmLocation osmLocation; + final bool popFirst; + + @override + Widget build(BuildContext context) { + const double markerSize = 50; + final LatLng latLng = osmLocation.getLatLng(); + final String? title = osmLocation.getTitle(); + final String? subtitle = osmLocation.getSubtitle(); + return SmoothScaffold( + appBar: SmoothAppBar( + title: title == null ? null : Text(title), + subTitle: subtitle == null ? null : Text(subtitle), + actions: [ + IconButton( + icon: const Icon(Icons.check), + onPressed: () { + // pops that map page + Navigator.of(context).pop(); + if (popFirst) { + // pops the result page + Navigator.of(context).pop(); + } + // returns the result + Navigator.of(context).pop(osmLocation); + }, + ), + IconButton( + icon: const Icon(Icons.info), + onPressed: () => showCupertinoModalPopup( + context: context, + builder: (final BuildContext context) => CupertinoActionSheet( + actions: [ + if (osmLocation.name != null) + _getItem(context, osmLocation.name!, 'Name'), + if (osmLocation.street != null) + _getItem(context, osmLocation.street!, 'Street'), + if (osmLocation.city != null) + _getItem(context, osmLocation.city!, 'City'), + if (osmLocation.postcode != null) + _getItem(context, osmLocation.postcode!, 'Postcode'), + if (osmLocation.country != null) + _getItem(context, osmLocation.country!, 'Country'), + _getItem( + context, + '${osmLocation.latitude}, ${osmLocation.longitude}', + 'Coordinates', + ), + ], + ), + ), + ), + ], + ), + body: FlutterMap( + options: MapOptions( + initialCenter: latLng, + initialZoom: 17, + ), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'org.openfoodfacts.app', + ), + MarkerLayer( + markers: [ + Marker( + point: latLng, + child: const Icon( + Icons.pin_drop, + color: Colors.lightBlue, + size: markerSize, + ), + alignment: Alignment.topCenter, + width: markerSize, + height: markerSize, + ), + ], + ), + RichAttributionWidget( + popupInitialDisplayDuration: const Duration(seconds: 5), + animationConfig: const ScaleRAWA(), + attributions: [ + TextSourceAttribution( + 'OpenStreetMap contributors', + onTap: () => LaunchUrlHelper.launchURL( + 'https://www.openstreetmap.org/copyright', + ), + ), + ], + ), + ], + ), + ); + } + + Widget _getItem( + final BuildContext context, + final String value, + final String label, + ) => + CupertinoActionSheetAction( + onPressed: () => Navigator.of(context).pop(), + child: Text('$label: $value'), + ); +} diff --git a/packages/smooth_app/lib/pages/locations/location_query_page.dart b/packages/smooth_app/lib/pages/locations/location_query_page.dart new file mode 100644 index 00000000000..61524e27a5e --- /dev/null +++ b/packages/smooth_app/lib/pages/locations/location_query_page.dart @@ -0,0 +1,171 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/location_query_model.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_error_card.dart'; +import 'package:smooth_app/pages/locations/search_location_preloaded_item.dart'; +import 'package:smooth_app/pages/product/common/loading_status.dart'; +import 'package:smooth_app/pages/product/common/search_app_bar_title.dart'; +import 'package:smooth_app/pages/product/common/search_empty_screen.dart'; +import 'package:smooth_app/pages/product/common/search_loading_screen.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Page that displays location results during and after download. +class LocationQueryPage extends StatefulWidget { + const LocationQueryPage({ + required this.query, + required this.editableAppBarTitle, + }); + + final String query; + final bool editableAppBarTitle; + + @override + State createState() => _LocationQueryPageState(); +} + +class _LocationQueryPageState extends State + with TraceableClientMixin { + late LocationQueryModel _model; + + @override + String get actionName => 'Opened location_search_page'; + + @override + void initState() { + super.initState(); + _model = LocationQueryModel(widget.query); + } + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final Size screenSize = MediaQuery.sizeOf(context); + final ThemeData themeData = Theme.of(context); + + return ChangeNotifierProvider.value( + value: _model, + builder: (BuildContext context, _) { + context.watch(); + + switch (_model.loadingStatus) { + case LoadingStatus.ERROR: + return _getErrorWidget( + screenSize, + themeData, + _model.loadingError ?? '', + ); + case LoadingStatus.LOADING: + if (_model.isEmpty()) { + return SearchLoadingScreen( + title: widget.query, + ); + } + break; + case LoadingStatus.LOADED: + if (_model.isEmpty()) { + return SearchEmptyScreen( + name: widget.query, + emptiness: _getEmptyText( + themeData, + appLocalizations.no_location_found, + ), + ); + } + break; + } + // Now used in two cases. + // 1. we have data downloaded and we display it (normal mode) + // 2. we are downloading extra data, and display what we already knew + return _getNotEmptyScreen( + screenSize, + themeData, + appLocalizations, + ); + }, + ); + } + + Widget _getNotEmptyScreen( + final Size screenSize, + final ThemeData themeData, + final AppLocalizations appLocalizations, + ) => + SmoothScaffold( + appBar: SmoothAppBar( + backgroundColor: themeData.scaffoldBackgroundColor, + elevation: 2, + automaticallyImplyLeading: false, + leading: const SmoothBackButton(), + title: SearchAppBarTitle( + title: widget.query, + editableAppBarTitle: widget.editableAppBarTitle, + ), + ), + body: ListTileTheme( + data: ListTileThemeData( + titleTextStyle: const TextStyle(fontSize: 20.0), + minLeadingWidth: 18.0, + iconColor: Theme.of(context).colorScheme.onSurface, + textColor: Theme.of(context).colorScheme.onSurface, + ), + child: ListView.builder( + itemBuilder: (BuildContext context, int index) => + SearchLocationPreloadedItem( + _model.displayedResults[index], + popFirst: true, + ).getWidget(context), + itemCount: _model.displayedResults.length, + ), + ), + ); + + Widget _getErrorWidget( + final Size screenSize, + final ThemeData themeData, + final String errorMessage, + ) { + return SearchEmptyScreen( + name: widget.query, + emptiness: Padding( + padding: const EdgeInsets.all(SMALL_SPACE), + child: SmoothErrorCard( + errorMessage: errorMessage, + tryAgainFunction: retryConnection, + ), + ), + ); + } + + Widget _getEmptyText( + final ThemeData themeData, + final String message, + ) => + Padding( + padding: const EdgeInsets.all(SMALL_SPACE), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: LARGE_SPACE), + child: Text( + message, + textAlign: TextAlign.center, + style: + themeData.textTheme.titleMedium!.copyWith(fontSize: 18.0), + ), + ), + ], + ), + ); + + void retryConnection() { + if (mounted) { + setState(() => _model = LocationQueryModel(widget.query)); + } + } +} diff --git a/packages/smooth_app/lib/pages/locations/osm_location.dart b/packages/smooth_app/lib/pages/locations/osm_location.dart new file mode 100644 index 00000000000..819e2391cbe --- /dev/null +++ b/packages/smooth_app/lib/pages/locations/osm_location.dart @@ -0,0 +1,73 @@ +import 'package:latlong2/latlong.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; + +/// Location as expected from OSM. +class OsmLocation { + const OsmLocation({ + required this.osmId, + required this.osmType, + required this.longitude, + required this.latitude, + this.name, + this.street, + this.city, + this.postcode, + this.country, + this.countryCode, + }); + + final int osmId; + final LocationOSMType osmType; + final double longitude; + final double latitude; + final String? name; + final String? street; + final String? city; + final String? postcode; + final String? country; + final String? countryCode; + + LatLng getLatLng() => LatLng(latitude, longitude); + + /// Returns a typical ListTile title text, or null if empty. + String? getTitle() { + final StringBuffer result = StringBuffer(); + if (name != null) { + result.write(name); + } + if (street != null) { + if (result.isNotEmpty) { + result.write(', '); + } + result.write(street); + } + if (result.isEmpty) { + return null; + } + return result.toString(); + } + + /// Returns a typical ListTile subtitle text, or null if empty. + String? getSubtitle() { + final StringBuffer result = StringBuffer(); + if (city != null) { + result.write(city); + } + if (postcode != null) { + if (result.isNotEmpty) { + result.write(', '); + } + result.write(postcode); + } + if (country != null) { + if (result.isNotEmpty) { + result.write(', '); + } + result.write(country); + } + if (result.isEmpty) { + return null; + } + return result.toString(); + } +} diff --git a/packages/smooth_app/lib/pages/locations/search_location_helper.dart b/packages/smooth_app/lib/pages/locations/search_location_helper.dart new file mode 100644 index 00000000000..c371bb098ef --- /dev/null +++ b/packages/smooth_app/lib/pages/locations/search_location_helper.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/database/dao_string_list.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/pages/locations/location_query_page.dart'; +import 'package:smooth_app/pages/product/common/search_helper.dart'; + +/// Search helper dedicated to location search. +class SearchLocationHelper extends SearchHelper { + const SearchLocationHelper(); + + @override + String get historyKey => DaoStringList.keySearchLocationHistory; + + @override + String getHintText(final AppLocalizations appLocalizations) => + 'Rechercher un magasin'; + + @override + void search( + BuildContext context, + String query, { + required SearchQueryCallback searchQueryCallback, + }) { + query = query.trim(); + if (query.isEmpty) { + return; + } + + final LocalDatabase localDatabase = context.read(); + addQuery(localDatabase, query); + + // await + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => LocationQueryPage( + query: query, + editableAppBarTitle: true, + ), + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/locations/search_location_model.dart b/packages/smooth_app/lib/pages/locations/search_location_model.dart new file mode 100644 index 00000000000..8de24528db3 --- /dev/null +++ b/packages/smooth_app/lib/pages/locations/search_location_model.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:smooth_app/data_models/product_list_supplier.dart'; +import 'package:smooth_app/pages/product/common/loading_status.dart'; + +/// Search location model. +class SearchLocationModel with ChangeNotifier { + SearchLocationModel(this._supplier) { + _asyncLoad(notify: true); + } + + ProductListSupplier _supplier; + + ProductListSupplier get supplier => _supplier; + + late LoadingStatus _loadingStatus; + String? _loadingError; + List displayBarcodes = []; + + bool isEmpty() => displayBarcodes.isEmpty; + + String? get loadingError => _loadingError; + LoadingStatus get loadingStatus => _loadingStatus; + + Future _asyncLoad({ + final bool notify = false, + final bool fromScratch = false, + }) async { + _loadingStatus = LoadingStatus.LOADING; + _loadingError = await supplier.asyncLoad(); + if (_loadingError != null) { + _loadingStatus = LoadingStatus.ERROR; + } else { + await _process(supplier.partialProductList.getBarcodes(), fromScratch); + _loadingStatus = LoadingStatus.LOADED; + } + if (notify) { + notifyListeners(); + } + return _loadingStatus == LoadingStatus.LOADED; + } + + Future loadNextPage() async { + final ProductListSupplier? refreshSupplier = supplier.getRefreshSupplier(); + if (refreshSupplier != null) { + // in that case, we were on a database supplier, on an empty page + _supplier = refreshSupplier; + } else { + // in that case, we were on a back-end supplier, on a loaded page + supplier.productQuery.toNextPage(); + } + return _asyncLoad(); + } + + Future _process( + final List barcodes, + final bool fromScratch, + ) async { + if (fromScratch) { + await supplier.clearBeyondTopPage(); + displayBarcodes.clear(); + } + displayBarcodes.addAll(barcodes); + _loadingStatus = LoadingStatus.LOADED; + } +} diff --git a/packages/smooth_app/lib/pages/locations/search_location_preloaded_item.dart b/packages/smooth_app/lib/pages/locations/search_location_preloaded_item.dart new file mode 100644 index 00000000000..a616b1dd51e --- /dev/null +++ b/packages/smooth_app/lib/pages/locations/search_location_preloaded_item.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/database/dao_osm_location.dart'; +import 'package:smooth_app/database/local_database.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/locations/location_map_page.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:smooth_app/pages/product/common/search_preloaded_item.dart'; + +/// Location search preloaded list item, for locations historically selected. +class SearchLocationPreloadedItem extends SearchPreloadedItem { + SearchLocationPreloadedItem(this.osmLocation, {required this.popFirst}); + + final OsmLocation osmLocation; + final bool popFirst; + + @override + Widget getWidget( + final BuildContext context, { + final VoidCallback? onDismissItem, + }) { + final String? title = osmLocation.getTitle(); + final String? subtitle = osmLocation.getSubtitle(); + final Widget child = SmoothCard( + child: ListTile( + onTap: () { + if (popFirst) { + // pops this result page + Navigator.of(context).pop(); + } + // returns the result from search page + Navigator.of(context).pop(osmLocation); + }, + title: title == null ? null : Text(title), + subtitle: subtitle == null ? null : Text(subtitle), + trailing: IconButton( + onPressed: () async => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => LocationMapPage( + osmLocation, + popFirst: popFirst, + ), + ), + ), + icon: const Icon(Icons.map), + ), + ), + ); + if (onDismissItem == null) { + return child; + } + return Dismissible( + key: Key('${osmLocation.osmType}${osmLocation.osmId}'), + direction: DismissDirection.endToStart, + onDismissed: (DismissDirection direction) async => onDismissItem(), + background: Container( + color: RED_COLOR, + alignment: AlignmentDirectional.centerEnd, + padding: const EdgeInsetsDirectional.only(end: LARGE_SPACE * 2), + child: const Icon( + Icons.delete, + color: Colors.white, + ), + ), + child: child, + ); + } + + @override + Future delete(final BuildContext context) async { + final LocalDatabase localDatabase = context.read(); + await DaoOsmLocation(localDatabase).delete(osmLocation); + } +} diff --git a/packages/smooth_app/lib/pages/navigator/app_navigator.dart b/packages/smooth_app/lib/pages/navigator/app_navigator.dart index fdc377a5fdd..68f33560b2b 100644 --- a/packages/smooth_app/lib/pages/navigator/app_navigator.dart +++ b/packages/smooth_app/lib/pages/navigator/app_navigator.dart @@ -3,11 +3,12 @@ import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/news_feed/newsfeed_provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/extension_on_text_helper.dart'; -import 'package:smooth_app/pages/carousel_manager.dart'; +import 'package:smooth_app/pages/guides/guide/guide_nutriscore_v2.dart'; import 'package:smooth_app/pages/navigator/error_page.dart'; import 'package:smooth_app/pages/navigator/external_page.dart'; import 'package:smooth_app/pages/onboarding/onboarding_flow_navigator.dart'; @@ -16,7 +17,10 @@ import 'package:smooth_app/pages/product/add_new_product_page.dart'; import 'package:smooth_app/pages/product/edit_product_page.dart'; import 'package:smooth_app/pages/product/new_product_page.dart'; import 'package:smooth_app/pages/product/product_loader_page.dart'; +import 'package:smooth_app/pages/scan/carousel/scan_carousel_manager.dart'; import 'package:smooth_app/pages/scan/search_page.dart'; +import 'package:smooth_app/pages/scan/search_product_helper.dart'; +import 'package:smooth_app/pages/user_management/sign_up_page.dart'; import 'package:smooth_app/query/product_query.dart'; /// A replacement for the [Navigator], where we internally use [GoRouter]. @@ -81,6 +85,7 @@ class AppNavigator extends InheritedWidget { /// - _product_creator Create a new product /// - _preferences User preferences /// - _search Search for a product +/// - _guides/ List of guides /// - _external Open an external link on the OFF website /// /// All our routes are prefixed with an underscore, as the [redirect] method @@ -114,6 +119,7 @@ class _SmoothGoRouter { return _findLastOnboardingPage(context); }, + // We use sub-routes to allow the back button to work correctly // for deep links to go back to the homepage routes: [ @@ -139,8 +145,8 @@ class _SmoothGoRouter { heroTag: state.uri.queryParameters['heroTag'], ); - if (ExternalCarouselManager.find(context) == null) { - return ExternalCarouselManager(child: widget); + if (ExternalScanCarouselManager.find(context) == null) { + return ExternalScanCarouselManager(child: widget); } else { return widget; } @@ -179,12 +185,6 @@ class _SmoothGoRouter { return AddNewProductPage.fromBarcode(barcode); }, ), - GoRoute( - path: _InternalAppRoutes.SEARCH_PAGE, - builder: (_, __) { - return SearchPage(); - }, - ), GoRoute( path: '${_InternalAppRoutes.PREFERENCES_PAGE}/:preferenceType', builder: (BuildContext context, GoRouterState state) { @@ -202,12 +202,37 @@ class _SmoothGoRouter { ); }, ), + GoRoute( + path: _InternalAppRoutes.SEARCH_PAGE, + builder: (_, __) => const SearchPage(SearchProductHelper()), + ), + GoRoute( + path: _InternalAppRoutes._GUIDES, + routes: [ + GoRoute( + path: _InternalAppRoutes.GUIDE_NUTRISCORE_V2_PAGE, + builder: (_, __) => const GuideNutriscoreV2(), + ), + ], + redirect: (_, GoRouterState state) { + if (state.uri.pathSegments.last != + _InternalAppRoutes.GUIDE_NUTRISCORE_V2_PAGE) { + return AppRoutes.EXTERNAL(state.path ?? ''); + } else { + return null; + } + }, + ), GoRoute( path: _InternalAppRoutes.EXTERNAL_PAGE, builder: (BuildContext context, GoRouterState state) { return ExternalPage(path: state.uri.queryParameters['path']!); }, ), + GoRoute( + path: _InternalAppRoutes.SIGNUP_PAGE, + builder: (_, __) => const SignUpPage(), + ) ], ), ], @@ -257,6 +282,10 @@ class _SmoothGoRouter { } } else if (path == _ExternalRoutes.MOBILE_APP_DOWNLOAD) { return AppRoutes.HOME; + } else if (path == _ExternalRoutes.GUIDE_NUTRISCORE_V2) { + return AppRoutes.GUIDE_NUTRISCORE_V2; + } else if (path == _ExternalRoutes.SIGNUP) { + return AppRoutes.SIGNUP; } else if (path != _InternalAppRoutes.HOME_PAGE) { externalLink = true; } @@ -284,6 +313,7 @@ class _SmoothGoRouter { // Must be set first to ensure the method is only called once _appLanguageInitialized = true; ProductQuery.setLanguage(context, context.read()); + context.read().loadLatestNews(); return context.read().refresh(); } @@ -366,11 +396,17 @@ class _InternalAppRoutes { static const String PREFERENCES_PAGE = '_preferences'; static const String SEARCH_PAGE = '_search'; static const String EXTERNAL_PAGE = '_external'; + static const String SIGNUP_PAGE = '_signup'; + + static const String _GUIDES = '_guides'; + static const String GUIDE_NUTRISCORE_V2_PAGE = '_nutriscore-v2'; } class _ExternalRoutes { static const String MOBILE_APP_DOWNLOAD = '/open-food-facts-mobile-app'; static const String PRODUCT_EDITION = '/cgi/product.pl'; + static const String GUIDE_NUTRISCORE_V2 = '/nutriscore-v2'; + static const String SIGNUP = '/signup'; } /// A list of internal routes to use with [AppNavigator] @@ -411,6 +447,11 @@ class AppRoutes { // Search view static String get SEARCH => '/${_InternalAppRoutes.SEARCH_PAGE}'; + // Guide for NutriScore (TODO: If we have more guides, we should use a more generic algorithm) + static String get GUIDE_NUTRISCORE_V2 => + '/${_InternalAppRoutes._GUIDES}/${_InternalAppRoutes.GUIDE_NUTRISCORE_V2_PAGE}'; + + static String get SIGNUP => '/${_InternalAppRoutes.SIGNUP_PAGE}'; // Open an external link (where path is relative to the OFF website) static String EXTERNAL(String path) => '/${_InternalAppRoutes.EXTERNAL_PAGE}/?path=$path'; diff --git a/packages/smooth_app/lib/pages/offline_data_page.dart b/packages/smooth_app/lib/pages/offline_data_page.dart index 2e4735fd65c..5cc440ffe93 100644 --- a/packages/smooth_app/lib/pages/offline_data_page.dart +++ b/packages/smooth_app/lib/pages/offline_data_page.dart @@ -32,7 +32,7 @@ class _OfflineDataPageState extends State { // TODO(ashaman999): replaace the header asset with a custom one for this page const String headerAsset = 'assets/preferences/main.svg'; final bool dark = Theme.of(context).brightness == Brightness.dark; - final double backgroundHeight = MediaQuery.of(context).size.height * .20; + final double backgroundHeight = MediaQuery.sizeOf(context).height * .20; final LocalDatabase localDatabase = context.watch(); final DaoProduct daoProduct = DaoProduct(localDatabase); final DaoProductLastAccess daoProductLastAccess = diff --git a/packages/smooth_app/lib/pages/onboarding/consent_analytics_page.dart b/packages/smooth_app/lib/pages/onboarding/consent_analytics_page.dart index 226e9f7a2b3..acadaf67225 100644 --- a/packages/smooth_app/lib/pages/onboarding/consent_analytics_page.dart +++ b/packages/smooth_app/lib/pages/onboarding/consent_analytics_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -20,75 +22,77 @@ class ConsentAnalyticsPage extends StatelessWidget { @override Widget build(BuildContext context) { - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); return ColoredBox( color: backgroundColor, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - SvgPicture.asset( - 'assets/onboarding/analytics.svg', - width: screenSize.width * .50, - package: AppHelper.APP_PACKAGE, - ), - const SizedBox(height: LARGE_SPACE), - AutoSizeText( - appLocalizations.consent_analytics_title, - maxLines: 2, - style: Theme.of(context) - .textTheme - .displayLarge! - .apply(color: const Color.fromARGB(255, 51, 51, 51)), - textAlign: TextAlign.center, - ), - const SizedBox(height: SMALL_SPACE), - AutoSizeText( - appLocalizations.consent_analytics_body1, - maxLines: 3, - textAlign: TextAlign.center, - style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, - ), - const SizedBox(height: SMALL_SPACE), - AutoSizeText( - appLocalizations.consent_analytics_body2, - maxLines: 3, - textAlign: TextAlign.center, - style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, - ), - ], + child: SafeArea( + bottom: Platform.isAndroid, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + 'assets/onboarding/analytics.svg', + width: screenSize.width * .50, + package: AppHelper.APP_PACKAGE, + ), + const SizedBox(height: LARGE_SPACE), + AutoSizeText( + appLocalizations.consent_analytics_title, + maxLines: 2, + style: Theme.of(context).textTheme.displayLarge!.apply( + color: const Color.fromARGB(255, 51, 51, 51)), + textAlign: TextAlign.center, + ), + const SizedBox(height: SMALL_SPACE), + AutoSizeText( + appLocalizations.consent_analytics_body1, + maxLines: 3, + textAlign: TextAlign.center, + style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, + ), + const SizedBox(height: SMALL_SPACE), + AutoSizeText( + appLocalizations.consent_analytics_body2, + maxLines: 3, + textAlign: TextAlign.center, + style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, + ), + ], + ), ), ), ), - ), - OnboardingBottomBar( - startButton: _buildButton( - context, - appLocalizations.refuse_button_label, - false, - const Color(0xFFA08D84), - Colors.white, - ), - endButton: _buildButton( - context, - appLocalizations.authorize_button_label, - true, - Colors.white, - Colors.black, + OnboardingBottomBar( + rightButton: _buildButton( + context, + appLocalizations.refuse_button_label, + false, + const Color(0xFFA08D84), + Colors.white, + ), + leftButton: _buildButton( + context, + appLocalizations.authorize_button_label, + true, + Colors.white, + Colors.black, + ), + backgroundColor: backgroundColor, + semanticsHorizontalOrder: false, ), - backgroundColor: backgroundColor, - ), - ], + ], + ), ), ); } diff --git a/packages/smooth_app/lib/pages/onboarding/country_selector.dart b/packages/smooth_app/lib/pages/onboarding/country_selector.dart index 9b9fb087910..ebc0d0f6a3d 100644 --- a/packages/smooth_app/lib/pages/onboarding/country_selector.dart +++ b/packages/smooth_app/lib/pages/onboarding/country_selector.dart @@ -17,15 +17,15 @@ class CountrySelector extends StatefulWidget { this.textStyle, this.padding, this.icon, - this.iconDecoration, this.inkWellBorderRadius, + required this.forceCurrencyChange, }); final TextStyle? textStyle; final EdgeInsetsGeometry? padding; final BorderRadius? inkWellBorderRadius; - final Icon? icon; - final BoxDecoration? iconDecoration; + final Widget? icon; + final bool forceCurrencyChange; @override State createState() => _CountrySelectorState(); @@ -99,7 +99,7 @@ class _CountrySelectorState extends State { prefixIcon: const Icon(Icons.search), controller: _countryController, onChanged: (String? query) { - query = query!.trim()..getComparisonSafeString(); + query = query!.trim().getComparisonSafeString(); setState( () { @@ -170,6 +170,13 @@ class _CountrySelectorState extends State { userPreferences, country.countryCode, ); + if (context.mounted) { + await _changeCurrencyIfRelevant( + country, + userPreferences, + context, + ); + } } }, child: DecoratedBox( @@ -199,16 +206,7 @@ class _CountrySelectorState extends State { ), ), ), - Container( - height: double.infinity, - decoration: - widget.iconDecoration ?? const BoxDecoration(), - child: AspectRatio( - aspectRatio: 1.0, - child: - widget.icon ?? const Icon(Icons.arrow_drop_down), - ), - ), + widget.icon ?? const Icon(Icons.arrow_drop_down), ], ), ), @@ -312,4 +310,50 @@ class _CountrySelectorState extends State { _countryController.dispose(); super.dispose(); } + + Future _changeCurrencyIfRelevant( + final Country country, + final UserPreferences userPreferences, + final BuildContext context, + ) async { + final OpenFoodFactsCountry? offCountry = + OpenFoodFactsCountry.fromOffTag(country.countryCode); + final String? possibleCurrencyCode = offCountry?.currency?.name; + if (possibleCurrencyCode == null) { + return; + } + bool? changeCurrency; + final String? currentCurrencyCode = userPreferences.userCurrencyCode; + if (currentCurrencyCode == null) { + changeCurrency = true; + } else if (widget.forceCurrencyChange) { + changeCurrency = true; + } else if (currentCurrencyCode != possibleCurrencyCode) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + changeCurrency = await showDialog( + context: context, + builder: (final BuildContext context) => SmoothAlertDialog( + body: Text( + '${appLocalizations.country_change_message}' + '\n' + '${appLocalizations.currency_auto_change_message( + currentCurrencyCode, + possibleCurrencyCode, + )}', + ), + negativeAction: SmoothActionButton( + onPressed: () => Navigator.of(context).pop(), + text: appLocalizations.no, + ), + positiveAction: SmoothActionButton( + onPressed: () => Navigator.of(context).pop(true), + text: appLocalizations.yes, + ), + ), + ); + } + if (changeCurrency == true) { + await userPreferences.setUserCurrencyCode(possibleCurrencyCode); + } + } } diff --git a/packages/smooth_app/lib/pages/onboarding/currency_selector.dart b/packages/smooth_app/lib/pages/onboarding/currency_selector.dart new file mode 100644 index 00000000000..84de4dadf12 --- /dev/null +++ b/packages/smooth_app/lib/pages/onboarding/currency_selector.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/onboarding/currency_selector_helper.dart'; +import 'package:smooth_app/pages/prices/currency_extension.dart'; + +/// A selector for selecting user's currency. +class CurrencySelector extends StatelessWidget { + CurrencySelector({ + this.textStyle, + this.padding, + this.icon, + }); + + final TextStyle? textStyle; + final EdgeInsetsGeometry? padding; + final Icon? icon; + + final CurrencySelectorHelper helper = CurrencySelectorHelper(); + + @override + Widget build(BuildContext context) { + final UserPreferences userPreferences = context.watch(); + final Currency selected = helper.getSelected( + userPreferences.userCurrencyCode, + ); + + return InkWell( + borderRadius: ANGULAR_BORDER_RADIUS, + onTap: () async { + final Currency? currency = await helper.openCurrencySelector( + context: context, + selected: selected, + ); + if (currency != null) { + await userPreferences.setUserCurrencyCode(currency.name); + } + }, + child: DecoratedBox( + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + child: IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: SMALL_SPACE, + ).add(padding ?? EdgeInsets.zero), + child: Icon(helper.currencyIconData), + ), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), + child: Text( + selected.getFullName(), + style: Theme.of(context) + .textTheme + .displaySmall + ?.merge(textStyle), + ), + ), + ), + icon ?? const Icon(Icons.arrow_drop_down), + ], + ), + ), + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/onboarding/currency_selector_helper.dart b/packages/smooth_app/lib/pages/onboarding/currency_selector_helper.dart new file mode 100644 index 00000000000..689dfb5527a --- /dev/null +++ b/packages/smooth_app/lib/pages/onboarding/currency_selector_helper.dart @@ -0,0 +1,130 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_text_form_field.dart'; +import 'package:smooth_app/pages/prices/currency_extension.dart'; +import 'package:smooth_app/widgets/smooth_text.dart'; + +/// Helper for currency selection. +class CurrencySelectorHelper { + CurrencySelectorHelper(); + + final List _currencyList = List.from(Currency.values); + + IconData get currencyIconData => CupertinoIcons.money_dollar_circle; + + Future openCurrencySelector({ + required final BuildContext context, + required final Currency selected, + }) async { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final ScrollController scrollController = ScrollController(); + final TextEditingController currencyController = TextEditingController(); + _reorderCurrencies(selected); + List filteredList = List.from(_currencyList); + return showDialog( + context: context, + builder: (BuildContext context) { + return StatefulBuilder( + builder: + (BuildContext context, void Function(VoidCallback fn) setState) { + const double horizontalPadding = 16.0 + SMALL_SPACE; + + return SmoothListAlertDialog( + title: appLocalizations.currency_selector_title, + header: SmoothTextFormField( + type: TextFieldTypes.PLAIN_TEXT, + prefixIcon: const Icon(Icons.search), + controller: currencyController, + onChanged: (String? query) { + query = query!.trim().getComparisonSafeString(); + + setState( + () { + filteredList = _currencyList + .where( + (Currency item) => item + .getFullName() + .getComparisonSafeString() + .contains( + query!, + ), + ) + .toList(growable: false); + }, + ); + }, + hintText: appLocalizations.search, + ), + scrollController: scrollController, + list: ListView.separated( + controller: scrollController, + itemBuilder: (BuildContext context, int index) { + final Currency currency = filteredList[index]; + final bool isSelected = currency == selected; + return ListTile( + dense: true, + contentPadding: const EdgeInsets.symmetric( + horizontal: horizontalPadding, + ), + trailing: isSelected ? const Icon(Icons.check) : null, + title: TextHighlighter( + text: currency.getFullName(), + filter: currencyController.text, + selected: isSelected, + ), + onTap: () { + Navigator.of(context).pop(currency); + currencyController.clear(); + }, + ); + }, + separatorBuilder: (_, __) => const Divider( + height: 1.0, + ), + itemCount: filteredList.length, + shrinkWrap: true, + ), + positiveAction: SmoothActionButton( + onPressed: () { + Navigator.pop(context); + currencyController.clear(); + }, + text: appLocalizations.cancel, + ), + ); + }, + ); + }, + ); + } + + Currency getSelected(final String? code) { + if (code != null) { + for (final Currency currency in _currencyList) { + if (currency.name == code) { + return currency; + } + } + } + return _currencyList[0]; + } + + /// Reorder currencies alphabetically, bring user's selected one to top. + void _reorderCurrencies(final Currency selected) { + _currencyList.sort( + (final Currency a, final Currency b) { + if (a == selected) { + return -1; + } + if (b == selected) { + return 1; + } + return a.name.compareTo(b.name); + }, + ); + } +} diff --git a/packages/smooth_app/lib/pages/onboarding/knowledge_panel_page_template.dart b/packages/smooth_app/lib/pages/onboarding/knowledge_panel_page_template.dart index 5fb4d28a655..7be4686b503 100644 --- a/packages/smooth_app/lib/pages/onboarding/knowledge_panel_page_template.dart +++ b/packages/smooth_app/lib/pages/onboarding/knowledge_panel_page_template.dart @@ -85,7 +85,7 @@ class _KnowledgePanelPageTemplateState product: _product, onboardingMode: true, ); - return Container( + return ColoredBox( color: widget.backgroundColor, child: SafeArea( bottom: Platform.isAndroid, @@ -103,8 +103,7 @@ class _KnowledgePanelPageTemplateState children: [ SvgPicture.asset( widget.svgAsset, - height: - MediaQuery.of(context).size.height * .25, + height: MediaQuery.sizeOf(context).height * .25, package: AppHelper.APP_PACKAGE, ), Padding( diff --git a/packages/smooth_app/lib/pages/onboarding/next_button.dart b/packages/smooth_app/lib/pages/onboarding/next_button.dart index 81e48e248dd..b8278c92a75 100644 --- a/packages/smooth_app/lib/pages/onboarding/next_button.dart +++ b/packages/smooth_app/lib/pages/onboarding/next_button.dart @@ -36,7 +36,7 @@ class NextButton extends StatelessWidget { OnboardingFlowNavigator(userPreferences); final OnboardingPage previousPage = currentPage.getPrevPage(); return OnboardingBottomBar( - startButton: previousPage.isOnboardingNotStarted() + rightButton: previousPage.isOnboardingNotStarted() ? null : OnboardingBottomIcon( onPressed: () async => navigator.navigateToPage( @@ -50,7 +50,7 @@ class NextButton extends StatelessWidget { ? const EdgeInsetsDirectional.only(end: 2.0) : EdgeInsets.zero, ), - endButton: OnboardingBottomButton( + leftButton: OnboardingBottomButton( onPressed: () async { await OnboardingLoader(localDatabase) .runAtNextTime(currentPage, context); diff --git a/packages/smooth_app/lib/pages/onboarding/onboarding_bottom_bar.dart b/packages/smooth_app/lib/pages/onboarding/onboarding_bottom_bar.dart index ea8042c1934..089aed5f5ef 100644 --- a/packages/smooth_app/lib/pages/onboarding/onboarding_bottom_bar.dart +++ b/packages/smooth_app/lib/pages/onboarding/onboarding_bottom_bar.dart @@ -1,16 +1,21 @@ import 'package:flutter/material.dart'; +import 'package:flutter/semantics.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; /// Bottom Bar during onboarding. Typical use case: previous/next buttons. class OnboardingBottomBar extends StatelessWidget { const OnboardingBottomBar({ - required this.endButton, + required this.leftButton, required this.backgroundColor, - this.startButton, + this.rightButton, + this.semanticsHorizontalOrder = true, }); - final Widget endButton; - final Widget? startButton; + final Widget leftButton; + final Widget? rightButton; + + /// If [true], the [leftButton] will be said first by the screen reader. + final bool semanticsHorizontalOrder; /// Color of the background where we put the buttons. /// @@ -19,10 +24,10 @@ class OnboardingBottomBar extends StatelessWidget { @override Widget build(BuildContext context) { - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); // Side padding is 8% of total width. final double sidePadding = screenSize.width * .08; - final bool hasPrevious = startButton != null; + final bool hasPrevious = rightButton != null; return Column( children: [ Container( @@ -43,8 +48,15 @@ class OnboardingBottomBar extends StatelessWidget { : MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (startButton != null) startButton!, - endButton, + if (rightButton != null) + Semantics( + sortKey: OrdinalSortKey(semanticsHorizontalOrder ? 1.0 : 2.0), + child: rightButton, + ), + Semantics( + sortKey: OrdinalSortKey(semanticsHorizontalOrder ? 2.0 : 1.0), + child: leftButton, + ), ], ), ), @@ -78,12 +90,11 @@ class OnboardingBottomButton extends StatelessWidget { key: nextKey, onPressed: onPressed, style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(backgroundColor), + backgroundColor: WidgetStateProperty.all(backgroundColor), overlayColor: backgroundColor == Colors.white - ? MaterialStateProperty.all( - Theme.of(context).splashColor) + ? WidgetStateProperty.all(Theme.of(context).splashColor) : null, - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(40))), ), diff --git a/packages/smooth_app/lib/pages/onboarding/onboarding_flow_navigator.dart b/packages/smooth_app/lib/pages/onboarding/onboarding_flow_navigator.dart index 9cd670c5ee9..2391b6abb1d 100644 --- a/packages/smooth_app/lib/pages/onboarding/onboarding_flow_navigator.dart +++ b/packages/smooth_app/lib/pages/onboarding/onboarding_flow_navigator.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/database/local_database.dart'; -import 'package:smooth_app/pages/carousel_manager.dart'; import 'package:smooth_app/pages/navigator/app_navigator.dart'; import 'package:smooth_app/pages/onboarding/consent_analytics_page.dart'; import 'package:smooth_app/pages/onboarding/permissions_page.dart'; @@ -10,17 +9,16 @@ import 'package:smooth_app/pages/onboarding/preferences_page.dart'; import 'package:smooth_app/pages/onboarding/reinvention_page.dart'; import 'package:smooth_app/pages/onboarding/sample_eco_card_page.dart'; import 'package:smooth_app/pages/onboarding/sample_health_card_page.dart'; -import 'package:smooth_app/pages/onboarding/scan_example.dart'; import 'package:smooth_app/pages/onboarding/welcome_page.dart'; import 'package:smooth_app/pages/page_manager.dart'; +import 'package:smooth_app/pages/scan/carousel/scan_carousel_manager.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; import 'package:smooth_app/widgets/will_pop_scope.dart'; enum OnboardingPage { NOT_STARTED, - REINVENTION, + HOME_PAGE, WELCOME, - SCAN_EXAMPLE, HEALTH_CARD_EXAMPLE, ECO_CARD_EXAMPLE, PREFERENCES_PAGE, @@ -52,12 +50,10 @@ enum OnboardingPage { Color getBackgroundColor() { switch (this) { case OnboardingPage.NOT_STARTED: - case OnboardingPage.REINVENTION: + case OnboardingPage.HOME_PAGE: return const Color(0xFFDFF4FF); case OnboardingPage.WELCOME: return const Color(0xFFFCFCFC); - case OnboardingPage.SCAN_EXAMPLE: - return const Color(0xFFE3F6FF); case OnboardingPage.HEALTH_CARD_EXAMPLE: return const Color(0xFFFFF1D1); case OnboardingPage.ECO_CARD_EXAMPLE: @@ -79,15 +75,10 @@ enum OnboardingPage { final Color backgroundColor = getBackgroundColor(); switch (this) { case OnboardingPage.NOT_STARTED: - case OnboardingPage.REINVENTION: - return ReinventionPage(backgroundColor); + case OnboardingPage.HOME_PAGE: + return const OnboardingHomePage(); case OnboardingPage.WELCOME: return WelcomePage(backgroundColor); - case OnboardingPage.SCAN_EXAMPLE: - return _wrapWidgetInCustomBackNavigator( - context, - ScanExample(backgroundColor), - ); case OnboardingPage.HEALTH_CARD_EXAMPLE: return _wrapWidgetInCustomBackNavigator( context, @@ -114,7 +105,7 @@ enum OnboardingPage { ConsentAnalyticsPage(backgroundColor), ); case OnboardingPage.ONBOARDING_COMPLETE: - return ExternalCarouselManager(child: PageManager()); + return ExternalScanCarouselManager(child: PageManager()); } } diff --git a/packages/smooth_app/lib/pages/onboarding/permissions_page.dart b/packages/smooth_app/lib/pages/onboarding/permissions_page.dart index c201f6bbaae..bfb2b669f48 100644 --- a/packages/smooth_app/lib/pages/onboarding/permissions_page.dart +++ b/packages/smooth_app/lib/pages/onboarding/permissions_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart' hide Listener; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -46,63 +48,65 @@ class _PermissionsPageState extends State { }, child: ColoredBox( color: widget.backgroundColor, - child: Column( - children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - LayoutBuilder(builder: - (BuildContext context, BoxConstraints constraints) { - return SizedBox.square( - dimension: constraints.maxWidth * 0.5, - child: Transform.rotate( - angle: -0.2, - child: const animations.BarcodeAnimation(), - ), - ); - }), - const SizedBox(height: LARGE_SPACE), - AutoSizeText( - appLocalizations.permissions_page_title, - maxLines: 2, - style: Theme.of(context) - .textTheme - .displayLarge! - .apply(color: const Color.fromARGB(255, 51, 51, 51)), - textAlign: TextAlign.center, - ), - const SizedBox(height: SMALL_SPACE), - AutoSizeText( - appLocalizations.permissions_page_body1, - maxLines: 2, - textAlign: TextAlign.center, - style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, - ), - const SizedBox(height: MEDIUM_SPACE), - AutoSizeText( - appLocalizations.permissions_page_body2, - maxLines: 3, - textAlign: TextAlign.center, - style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, - ), - ], + child: SafeArea( + bottom: Platform.isAndroid, + child: Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + LayoutBuilder(builder: + (BuildContext context, BoxConstraints constraints) { + return SizedBox.square( + dimension: constraints.maxWidth * 0.5, + child: Transform.rotate( + angle: -0.2, + child: const animations.BarcodeAnimation(), + ), + ); + }), + const SizedBox(height: LARGE_SPACE), + AutoSizeText( + appLocalizations.permissions_page_title, + maxLines: 2, + style: Theme.of(context).textTheme.displayLarge!.apply( + color: const Color.fromARGB(255, 51, 51, 51)), + textAlign: TextAlign.center, + ), + const SizedBox(height: SMALL_SPACE), + AutoSizeText( + appLocalizations.permissions_page_body1, + maxLines: 2, + textAlign: TextAlign.center, + style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, + ), + const SizedBox(height: MEDIUM_SPACE), + AutoSizeText( + appLocalizations.permissions_page_body2, + maxLines: 3, + textAlign: TextAlign.center, + style: WellSpacedTextHelper.TEXT_STYLE_WITH_WELL_SPACED, + ), + ], + ), + ), + )), + OnboardingBottomBar( + rightButton: _IgnoreButton( + onPermissionIgnored: () => _moveToNextScreen(context), + ), + leftButton: _AskPermissionButton( + onPermissionIgnored: () => _moveToNextScreen(context), ), - ), - )), - OnboardingBottomBar( - startButton: _IgnoreButton( - onPermissionIgnored: () => _moveToNextScreen(context), - ), - endButton: _AskPermissionButton( - onPermissionIgnored: () => _moveToNextScreen(context), - ), - backgroundColor: widget.backgroundColor, - ) - ], + backgroundColor: widget.backgroundColor, + semanticsHorizontalOrder: false, + ) + ], + ), ), ), ); diff --git a/packages/smooth_app/lib/pages/onboarding/preferences_page.dart b/packages/smooth_app/lib/pages/onboarding/preferences_page.dart index 7fba594ab08..6f7d4445b10 100644 --- a/packages/smooth_app/lib/pages/onboarding/preferences_page.dart +++ b/packages/smooth_app/lib/pages/onboarding/preferences_page.dart @@ -82,7 +82,7 @@ class _HelperState extends State<_Helper> { final List pageData = [ SvgPicture.asset( 'assets/onboarding/preferences.svg', - height: MediaQuery.of(context).size.height * .25, + height: MediaQuery.sizeOf(context).height * .25, package: AppHelper.APP_PACKAGE, ), Padding( @@ -109,6 +109,7 @@ class _HelperState extends State<_Helper> { isRemovable: false, isSettingVisible: false, isProductEditable: false, + showQuestionsBanner: false, ), ), ]; diff --git a/packages/smooth_app/lib/pages/onboarding/reinvention_page.dart b/packages/smooth_app/lib/pages/onboarding/reinvention_page.dart index a8ec8f470b7..690c8833d61 100644 --- a/packages/smooth_app/lib/pages/onboarding/reinvention_page.dart +++ b/packages/smooth_app/lib/pages/onboarding/reinvention_page.dart @@ -1,198 +1,277 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; import 'package:rive/rive.dart'; -import 'package:sensors_plus/sensors_plus.dart'; -import 'package:smooth_app/generic_lib/design_constants.dart'; -import 'package:smooth_app/generic_lib/duration_constants.dart'; -import 'package:smooth_app/helpers/app_helper.dart'; -import 'package:smooth_app/pages/onboarding/next_button.dart'; +import 'package:smooth_app/data_models/onboarding_loader.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/pages/onboarding/onboarding_flow_navigator.dart'; +import 'package:smooth_app/pages/onboarding/v2/onboarding_bottom_hills.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; +import 'package:smooth_app/widgets/smooth_text.dart'; /// Onboarding page: "reinvention" -class ReinventionPage extends StatelessWidget { - const ReinventionPage(this.backgroundColor); - - final Color backgroundColor; +class OnboardingHomePage extends StatelessWidget { + const OnboardingHomePage({super.key}); @override Widget build(BuildContext context) { - const double muchTooBigFontSize = 150; - final AppLocalizations appLocalizations = AppLocalizations.of(context); - final TextStyle headlineStyle = Theme.of(context) - .textTheme - .displayMedium! - .copyWith(fontSize: muchTooBigFontSize); - final Size screenSize = MediaQuery.of(context).size; - final double animHeight = 352.0 * screenSize.width / 375.0; - - return Container( - color: backgroundColor, - child: SafeArea( - bottom: false, + return Scaffold( + backgroundColor: const Color(0xFFE3F3FE), + body: Provider.value( + value: OnboardingConfig._(MediaQuery.of(context)), child: Stack( children: [ - Positioned( - left: 0.0, - right: 0.0, - bottom: 0.0, - top: screenSize.height * 0.75, - child: Background( - screenWidth: screenSize.width, - ), - ), - Positioned( - left: 0.0, - right: 0.0, - bottom: 0.0, - child: RepaintBoundary( - child: SizedBox( - width: screenSize.width, - height: animHeight, - child: const RiveAnimation.asset( - 'assets/onboarding/onboarding.riv', - artboard: 'Reinvention', - animations: ['Loop'], - alignment: Alignment.bottomCenter, - ), - ), - ), + const _OnboardingWelcomePageContent(), + OnboardingBottomHills( + onTap: () async { + final UserPreferences userPreferences = + context.read(); + final LocalDatabase localDatabase = + context.read(); + + await OnboardingLoader(localDatabase) + .runAtNextTime(OnboardingPage.HOME_PAGE, context); + if (context.mounted) { + await OnboardingFlowNavigator(userPreferences).navigateToPage( + context, + OnboardingPage.HOME_PAGE.getNextPage(), + ); + } + }, ), - Positioned( - top: 0.0, - left: 0.0, - right: 0.0, - bottom: animHeight - 20.0, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Flexible( - flex: 30, - child: Padding( - padding: const EdgeInsets.all(SMALL_SPACE), - child: Center( - child: AutoSizeText( - appLocalizations.onboarding_reinventing_text1, - style: headlineStyle, - maxLines: 3, - textAlign: TextAlign.center, - ), - ), - ), - ), - Flexible( - flex: 15, - child: SvgPicture.asset( - 'assets/onboarding/birthday-cake.svg', - package: AppHelper.APP_PACKAGE, - ), - ), - Flexible( - flex: 30, - child: Padding( - padding: const EdgeInsets.all(SMALL_SPACE), - child: Center( - child: AutoSizeText( - appLocalizations.onboarding_reinventing_text2, - style: headlineStyle, - maxLines: 3, - textAlign: TextAlign.center, - ), - ), - ), - ), - Flexible( - flex: 25, - child: SvgPicture.asset( - 'assets/onboarding/title.svg', - package: AppHelper.APP_PACKAGE, - ), - ), - ], + ], + ), + ), + ); + } +} + +class _OnboardingWelcomePageContent extends StatelessWidget { + const _OnboardingWelcomePageContent(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final double fontMultiplier = OnboardingConfig.of(context).fontMultiplier; + final double hillsHeight = OnboardingBottomHills.height(context); + + return Padding( + padding: EdgeInsetsDirectional.only( + top: hillsHeight * 0.5 + MediaQuery.viewPaddingOf(context).top, + bottom: hillsHeight, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 15, + child: Text( + appLocalizations.onboarding_home_welcome_text1, + style: TextStyle( + fontSize: 45 * fontMultiplier, + fontWeight: FontWeight.bold, ), + textAlign: TextAlign.center, ), - Positioned( - bottom: 0, - child: SafeArea( - bottom: !Platform.isIOS, - child: const NextButton( - OnboardingPage.REINVENTION, - backgroundColor: null, - nextKey: Key('nextAfterReinvention'), + ), + const Expanded( + flex: 37, + child: _SunAndCloud(), + ), + Expanded( + flex: 45, + child: FractionallySizedBox( + widthFactor: 0.65, + child: Align( + alignment: const Alignment(0, -0.2), + child: OnboardingText( + text: appLocalizations.onboarding_home_welcome_text2, ), ), ), - ], - ), + ), + ], ), ); } } -class Background extends StatefulWidget { - const Background({required this.screenWidth}); - - final double screenWidth; +class _SunAndCloud extends StatefulWidget { + const _SunAndCloud(); @override - State createState() => _BackgroundState(); + State<_SunAndCloud> createState() => _SunAndCloudState(); } -class _BackgroundState extends State { - StreamSubscription? _subscription; - double parallax = 0.0; +class _SunAndCloudState extends State<_SunAndCloud> + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; @override void initState() { super.initState(); - - if (Platform.isAndroid || Platform.isIOS) { - _subscription = gyroscopeEvents.listen((GyroscopeEvent event) { - setState(() => parallax = event.y); - }); - } + _controller = + AnimationController(vsync: this, duration: const Duration(seconds: 2)) + ..addListener(() => setState(() {})); + _animation = Tween( + begin: -1.0, + end: 1.0, + ).animate(_controller); + _controller.repeat(reverse: true); } @override Widget build(BuildContext context) { - return SizedBox.expand( - child: RepaintBoundary( - child: Stack( + final TextDirection textDirection = Directionality.of(context); + + return RepaintBoundary( + child: LayoutBuilder(builder: ( + BuildContext context, + BoxConstraints constraints, + ) { + return Stack( children: [ - AnimatedPositioned( - bottom: 0.0, - right: (-15.0 * parallax).clamp(-30.0, 0.0), - width: widget.screenWidth * 0.808, - duration: SmoothAnimationsDuration.short, - child: SvgPicture.asset( - 'assets/onboarding/hill_end.svg', - fit: BoxFit.fill, - ), + Positioned.directional( + top: constraints.maxHeight * 0.3, + bottom: constraints.maxHeight * 0.2, + start: (_animation.value * 161.0) * 0.3, + textDirection: textDirection, + child: SvgPicture.asset('assets/onboarding/cloud.svg'), ), - AnimatedPositioned( - bottom: 0.0, - left: (-10.0 * parallax).clamp(-20.0, 0.0), - width: widget.screenWidth * 0.855, - duration: SmoothAnimationsDuration.short, - child: SvgPicture.asset( - 'assets/onboarding/hill_start.svg', - fit: BoxFit.fill, + const Align( + alignment: Alignment.center, + child: RiveAnimation.asset( + 'assets/animations/off.riv', + artboard: 'Success', + animations: ['Timeline 1'], ), - ) + ), + Positioned.directional( + top: constraints.maxHeight * 0.22, + bottom: constraints.maxHeight * 0.35, + end: (_animation.value * 40.0) - 31, + textDirection: textDirection, + child: SvgPicture.asset('assets/onboarding/cloud.svg'), + ), ], - ), - ), + ); + }), ); } @override void dispose() { - _subscription?.cancel(); + _controller.dispose(); super.dispose(); } } + +class OnboardingText extends StatelessWidget { + const OnboardingText({ + required this.text, + this.margin, + super.key, + }); + + final String text; + final EdgeInsetsGeometry? margin; + + @override + Widget build(BuildContext context) { + double fontMultiplier; + try { + fontMultiplier = OnboardingConfig.of(context).fontMultiplier; + } catch (_) { + fontMultiplier = + OnboardingConfig.computeFontMultiplier(MediaQuery.of(context)); + } + + final Color backgroundColor = + Theme.of(context).extension()!.orange; + + return RichText( + text: TextSpan( + children: _extractChunks().map(((String text, bool highlighted) el) { + if (el.$2) { + return _createSpan( + el.$1, + 30 * fontMultiplier, + backgroundColor, + ); + } else { + return TextSpan(text: el.$1); + } + }).toList(growable: false), + style: DefaultTextStyle.of(context).style.copyWith( + fontSize: 30 * fontMultiplier, + height: 1.53, + fontWeight: FontWeight.w600, + ), + ), + textAlign: TextAlign.center, + ); + } + + Iterable<(String, bool)> _extractChunks() { + final Iterable matches = + RegExp(r'\*\*(.*?)\*\*').allMatches(text); + + if (matches.length <= 1) { + return <(String, bool)>[(text, false)]; + } + + final List<(String, bool)> chunks = <(String, bool)>[]; + + int lastMatch = 0; + + for (final RegExpMatch match in matches) { + if (matches.first.start > 0) { + chunks.add((text.substring(lastMatch, match.start), false)); + } + + chunks.add((text.substring(match.start + 2, match.end - 2), true)); + lastMatch = match.end; + } + + if (lastMatch < text.length) { + chunks.add((text.substring(lastMatch), false)); + } + + return chunks; + } + + WidgetSpan _createSpan(String text, double fontSize, Color backgroundColor) => + HighlightedTextSpan( + text: text, + textStyle: TextStyle( + color: Colors.white, + fontSize: fontSize, + fontWeight: FontWeight.w700, + ), + padding: const EdgeInsetsDirectional.only( + top: 1.0, + bottom: 5.0, + start: 15.0, + end: 15.0, + ), + margin: margin ?? const EdgeInsetsDirectional.symmetric(vertical: 2.5), + backgroundColor: backgroundColor, + radius: 30.0, + ); +} + +// TODO(g123k): Move elsewhere when the onboarding will be redesigned +class OnboardingConfig { + OnboardingConfig._(MediaQueryData mediaQuery) + : fontMultiplier = computeFontMultiplier(mediaQuery); + final double fontMultiplier; + + static double computeFontMultiplier(MediaQueryData mediaQuery) => + ((mediaQuery.size.width * 45) / 428) / 45; + + static OnboardingConfig of(BuildContext context) => + context.watch(); +} diff --git a/packages/smooth_app/lib/pages/onboarding/scan_example.dart b/packages/smooth_app/lib/pages/onboarding/scan_example.dart deleted file mode 100644 index 7a8bcd49f12..00000000000 --- a/packages/smooth_app/lib/pages/onboarding/scan_example.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:smooth_app/generic_lib/design_constants.dart'; -import 'package:smooth_app/helpers/app_helper.dart'; -import 'package:smooth_app/pages/onboarding/next_button.dart'; -import 'package:smooth_app/pages/onboarding/onboarding_flow_navigator.dart'; -import 'package:smooth_app/widgets/smooth_text.dart'; - -/// Example explanation on how to scan a product. -class ScanExample extends StatelessWidget { - const ScanExample(this.backgroundColor); - - final Color backgroundColor; - - @override - Widget build(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - final Size screenSize = MediaQuery.of(context).size; - return Container( - color: backgroundColor, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Used for spacing - EMPTY_WIDGET, - Padding( - padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/onboarding/scan.svg', - height: screenSize.height * .50, - package: AppHelper.APP_PACKAGE, - ), - Padding( - padding: const EdgeInsetsDirectional.only(top: SMALL_SPACE), - child: SizedBox( - height: screenSize.height * .15, - child: AutoSizeText( - appLocalizations.offUtility, - maxLines: 2, - style: Theme.of(context) - .textTheme - .displayLarge! - .wellSpaced - .apply(color: const Color.fromARGB(255, 51, 51, 51)), - ), - ), - ), - ], - ), - ), - NextButton( - OnboardingPage.SCAN_EXAMPLE, - backgroundColor: backgroundColor, - nextKey: const Key('nextAfterScanExample'), - ), - ], - ), - ); - } -} diff --git a/packages/smooth_app/lib/pages/onboarding/v2/onboarding_bottom_hills.dart b/packages/smooth_app/lib/pages/onboarding/v2/onboarding_bottom_hills.dart new file mode 100644 index 00000000000..e8ecaa7c70b --- /dev/null +++ b/packages/smooth_app/lib/pages/onboarding/v2/onboarding_bottom_hills.dart @@ -0,0 +1,114 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/resources/app_icons.dart' as icons; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; + +class OnboardingBottomHills extends StatelessWidget { + const OnboardingBottomHills({ + required this.onTap, + super.key, + }); + + final VoidCallback onTap; + + static double height(BuildContext context) { + final double screenHeight = MediaQuery.sizeOf(context).height; + final double bottomPadding = MediaQuery.viewPaddingOf(context).bottom; + return screenHeight * (0.12 + (bottomPadding / screenHeight)); + } + + @override + Widget build(BuildContext context) { + final TextDirection textDirection = Directionality.of(context); + final double bottomPadding = MediaQuery.viewPaddingOf(context).bottom; + final double maxHeight = OnboardingBottomHills.height(context); + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + + return Positioned( + top: null, + bottom: 0.0, + left: 0.0, + right: 0.0, + height: maxHeight, + child: SizedBox( + child: Stack( + children: [ + Positioned.directional( + start: 0.0, + bottom: 0.0, + textDirection: textDirection, + child: SvgPicture.asset( + 'assets/onboarding/hill_start.svg', + height: maxHeight, + ), + ), + Positioned.directional( + end: 0.0, + bottom: 0.0, + textDirection: textDirection, + child: SvgPicture.asset( + 'assets/onboarding/hill_end.svg', + height: maxHeight * 0.965, + ), + ), + Positioned.directional( + textDirection: textDirection, + bottom: bottomPadding + (Platform.isIOS ? 0.0 : 15.0), + end: 15.0, + child: TextButton( + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all( + Colors.white, + ), + padding: WidgetStateProperty.all( + const EdgeInsetsDirectional.only( + start: LARGE_SPACE + 1.0, + end: LARGE_SPACE, + top: SMALL_SPACE, + bottom: SMALL_SPACE, + ), + ), + elevation: WidgetStateProperty.all(4.0), + iconColor: WidgetStateProperty.all( + colors.orange, + ), + foregroundColor: WidgetStateProperty.all( + colors.orange, + ), + iconSize: WidgetStateProperty.all(21.0), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20.0), + ), + ), + shadowColor: WidgetStateProperty.all( + Colors.black.withOpacity(0.50), + ), + ), + onPressed: onTap, + child: Row( + children: [ + Text( + AppLocalizations.of(context).onboarding_continue_button, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 22.0, + ), + ), + const SizedBox(width: LARGE_SPACE), + const icons.Arrow.right(), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/onboarding/welcome_page.dart b/packages/smooth_app/lib/pages/onboarding/welcome_page.dart index cc59c5da71b..736f8ba6425 100644 --- a/packages/smooth_app/lib/pages/onboarding/welcome_page.dart +++ b/packages/smooth_app/lib/pages/onboarding/welcome_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -22,115 +24,125 @@ class WelcomePage extends StatelessWidget { final ThemeData theme = Theme.of(context); final TextStyle headlineStyle = theme.textTheme.displayMedium!.wellSpaced; final TextStyle bodyTextStyle = theme.textTheme.bodyLarge!.wellSpaced; - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); return SmoothScaffold( backgroundColor: backgroundColor, brightness: Brightness.dark, resizeToAvoidBottomInset: false, - body: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - flex: 1, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), - child: ListView( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: screenSize.height * .05), - SvgPicture.asset( - 'assets/onboarding/title.svg', - height: screenSize.height * .10, - package: AppHelper.APP_PACKAGE, - ), - SvgPicture.asset( - 'assets/onboarding/globe.svg', - height: screenSize.height * .20, - package: AppHelper.APP_PACKAGE, - ), - Padding( - padding: - const EdgeInsetsDirectional.only(top: SMALL_SPACE), - child: SizedBox( - height: screenSize.height * .15, - child: AutoSizeText( - appLocalizations.whatIsOff, - style: headlineStyle, - maxLines: 3, - textAlign: TextAlign.center, - ), + body: SafeArea( + bottom: Platform.isAndroid, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + flex: 1, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: LARGE_SPACE), + child: ListView( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: screenSize.height * .05), + SvgPicture.asset( + 'assets/onboarding/title.svg', + height: screenSize.height * .10, + package: AppHelper.APP_PACKAGE, ), - ), - ], - ), - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - appLocalizations.onboarding_country_chooser_label, - style: bodyTextStyle, - ), - Padding( - padding: - const EdgeInsets.symmetric(vertical: MEDIUM_SPACE), - child: Ink( - decoration: BoxDecoration( - border: Border.fromBorderSide( - BorderSide( - color: theme.colorScheme.inversePrimary, - width: 1, - ), + SvgPicture.asset( + 'assets/onboarding/globe.svg', + height: screenSize.height * .20, + package: AppHelper.APP_PACKAGE, + ), + Padding( + padding: const EdgeInsetsDirectional.only( + top: SMALL_SPACE), + child: SizedBox( + height: screenSize.height * .15, + child: AutoSizeText( + appLocalizations.whatIsOff, + style: headlineStyle, + maxLines: 3, + textAlign: TextAlign.center, ), - borderRadius: ROUNDED_BORDER_RADIUS, - color: theme.colorScheme.onPrimary, ), - child: SizedBox( - width: double.infinity, - child: CountrySelector( - padding: const EdgeInsets.symmetric( - horizontal: SMALL_SPACE, - ), - inkWellBorderRadius: ROUNDED_BORDER_RADIUS, - icon: Icon( - Icons.edit, - color: Colors.white.withOpacity(0.9), + ), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + appLocalizations.onboarding_country_chooser_label, + style: bodyTextStyle, + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: MEDIUM_SPACE), + child: Ink( + decoration: BoxDecoration( + border: Border.fromBorderSide( + BorderSide( + color: theme.colorScheme.inversePrimary, + width: 1, + ), ), - iconDecoration: BoxDecoration( - color: theme.primaryColor, - borderRadius: ROUNDED_BORDER_RADIUS, + borderRadius: ROUNDED_BORDER_RADIUS, + color: theme.colorScheme.onPrimary, + ), + child: SizedBox( + width: double.infinity, + child: CountrySelector( + forceCurrencyChange: true, + padding: const EdgeInsets.symmetric( + horizontal: SMALL_SPACE, + ), + inkWellBorderRadius: ROUNDED_BORDER_RADIUS, + icon: Container( + height: double.infinity, + decoration: BoxDecoration( + color: theme.primaryColor, + borderRadius: ROUNDED_BORDER_RADIUS, + ), + child: AspectRatio( + aspectRatio: 1.0, + child: Icon( + Icons.edit, + color: Colors.white.withOpacity(0.9), + ), + ), + ), + textStyle: TextStyle(color: theme.primaryColor), ), - textStyle: TextStyle(color: theme.primaryColor), ), ), ), - ), - Padding( - padding: const EdgeInsetsDirectional.only( - bottom: VERY_SMALL_SPACE, - ), - child: Text( - appLocalizations.country_selection_explanation, - style: bodyTextStyle, + Padding( + padding: const EdgeInsetsDirectional.only( + bottom: VERY_SMALL_SPACE, + ), + child: Text( + appLocalizations.country_selection_explanation, + style: bodyTextStyle, + ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), - ), - NextButton( - OnboardingPage.WELCOME, - backgroundColor: backgroundColor, - nextKey: const Key('nextAfterWelcome'), - ), - ], + NextButton( + OnboardingPage.WELCOME, + backgroundColor: backgroundColor, + nextKey: const Key('nextAfterWelcome'), + ), + ], + ), ), ); } diff --git a/packages/smooth_app/lib/pages/page_manager.dart b/packages/smooth_app/lib/pages/page_manager.dart index 615ee2500fb..5ef62fcfec1 100644 --- a/packages/smooth_app/lib/pages/page_manager.dart +++ b/packages/smooth_app/lib/pages/page_manager.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; -import 'package:smooth_app/pages/carousel_manager.dart'; import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart'; +import 'package:smooth_app/pages/scan/carousel/scan_carousel_manager.dart'; import 'package:smooth_app/widgets/tab_navigator.dart'; import 'package:smooth_app/widgets/will_pop_scope.dart'; @@ -62,8 +62,8 @@ class PageManagerState extends State { @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); - final ExternalCarouselManagerState carouselManager = - ExternalCarouselManager.watch(context); + final ExternalScanCarouselManagerState carouselManager = + ExternalScanCarouselManager.watch(context); if (carouselManager.forceShowScannerTab) { _currentPage = BottomNavigationTab.Scan; diff --git a/packages/smooth_app/lib/pages/personalized_ranking_page.dart b/packages/smooth_app/lib/pages/personalized_ranking_page.dart index b18045efb89..4b6d42ea9d8 100644 --- a/packages/smooth_app/lib/pages/personalized_ranking_page.dart +++ b/packages/smooth_app/lib/pages/personalized_ranking_page.dart @@ -12,6 +12,7 @@ import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dar import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/duration_constants.dart'; import 'package:smooth_app/helpers/product_compatibility_helper.dart'; +import 'package:smooth_app/pages/product/common/loading_status.dart'; import 'package:smooth_app/pages/product/common/product_list_item_simple.dart'; import 'package:smooth_app/pages/product_list_user_dialog_helper.dart'; import 'package:smooth_app/widgets/smooth_app_bar.dart'; diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart index 6b177cb067c..3121ddff96c 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_account.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; @@ -16,6 +17,13 @@ import 'package:smooth_app/pages/preferences/account_deletion_webview.dart'; import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; +import 'package:smooth_app/pages/prices/get_prices_model.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; +import 'package:smooth_app/pages/prices/price_user_button.dart'; +import 'package:smooth_app/pages/prices/prices_page.dart'; +import 'package:smooth_app/pages/prices/prices_proofs_page.dart'; +import 'package:smooth_app/pages/prices/prices_users_page.dart'; +import 'package:smooth_app/pages/prices/product_price_add_page.dart'; import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; import 'package:smooth_app/pages/user_management/login_page.dart'; import 'package:smooth_app/query/paged_product_query.dart'; @@ -97,7 +105,7 @@ class UserPreferencesAccount extends AbstractUserPreferences { return null; } final ThemeData theme = Theme.of(context); - final Size size = MediaQuery.of(context).size; + final Size size = MediaQuery.sizeOf(context); return Container( margin: EdgeInsets.only( left: size.width / 4, @@ -131,7 +139,7 @@ class UserPreferencesAccount extends AbstractUserPreferences { List getChildren() { if (OpenFoodAPIConfiguration.globalUser == null) { // No credentials - final Size size = MediaQuery.of(context).size; + final Size size = MediaQuery.sizeOf(context); return [ UserPreferencesItemSimple( labels: [appLocalizations.sign_in], @@ -139,10 +147,10 @@ class UserPreferencesAccount extends AbstractUserPreferences { child: ElevatedButton( onPressed: () async => _goToLoginPage(), style: ButtonStyle( - minimumSize: MaterialStateProperty.all( + minimumSize: WidgetStateProperty.all( Size(size.width * 0.5, themeData.buttonTheme.height + 10), ), - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: CIRCULAR_BORDER_RADIUS, ), @@ -210,6 +218,91 @@ class UserPreferencesAccount extends AbstractUserPreferences { localDatabase: localDatabase, myCount: _getMyCount(UserSearchType.TO_BE_COMPLETED), ), + _getListTile( + PriceUserButton.showUserTitle( + user: ProductQuery.getWriteUser().userId, + context: context, + ), + () async => PriceUserButton.showUserPrices( + user: ProductQuery.getWriteUser().userId, + context: context, + ), + CupertinoIcons.money_dollar_circle, + myCount: _getPricesCount(owner: ProductQuery.getWriteUser().userId), + ), + _getListTile( + appLocalizations.user_search_proofs_title, + () async => Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => const PricesProofsPage(), + ), + ), + Icons.receipt, + ), + _getListTile( + appLocalizations.prices_add_a_receipt, + () async => ProductPriceAddPage.showProductPage( + context: context, + product: PriceMetaProduct.empty(), + proofType: ProofType.receipt, + ), + Icons.add_shopping_cart, + ), + _getListTile( + appLocalizations.prices_add_price_tags, + () async => ProductPriceAddPage.showProductPage( + context: context, + product: PriceMetaProduct.empty(), + proofType: ProofType.priceTag, + ), + Icons.add_shopping_cart, + ), + _getListTile( + appLocalizations.all_search_prices_latest_title, + () async => Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => PricesPage( + GetPricesModel( + parameters: GetPricesParameters() + ..orderBy = >[ + const OrderBy( + field: GetPricesOrderField.created, + ascending: false, + ), + ] + ..pageSize = GetPricesModel.pageSize + ..pageNumber = 1, + displayOwner: true, + displayProduct: true, + uri: OpenPricesAPIClient.getUri( + path: 'app/prices', + uriHelper: ProductQuery.uriProductHelper, + ), + title: appLocalizations.all_search_prices_latest_title, + ), + ), + ), + ), + CupertinoIcons.money_dollar_circle, + myCount: _getPricesCount(), + ), + _getListTile( + appLocalizations.all_search_prices_top_user_title, + () async => Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => const PricesUsersPage(), + ), + ), + Icons.account_box, + ), + _getPriceListTile( + appLocalizations.all_search_prices_top_location_title, + 'app/locations', + ), + _getPriceListTile( + appLocalizations.all_search_prices_top_product_title, + 'app/products', + ), _buildProductQueryTile( productQuery: PagedToBeCompletedProductQuery(), title: appLocalizations.all_search_to_be_completed_title, @@ -261,6 +354,21 @@ class UserPreferencesAccount extends AbstractUserPreferences { ]; } + UserPreferencesItem _getPriceListTile( + final String title, + final String path, + ) => + _getListTile( + title, + () async => LaunchUrlHelper.launchURL( + OpenPricesAPIClient.getUri( + path: path, + uriHelper: ProductQuery.uriProductHelper, + ).toString(), + ), + Icons.open_in_new, + ); + Future _confirmLogout() async => showDialog( context: context, builder: (BuildContext context) { @@ -311,6 +419,20 @@ class UserPreferencesAccount extends AbstractUserPreferences { } } + Future _getPricesCount({final String? owner}) async { + final MaybeError result = + await OpenPricesAPIClient.getPrices( + GetPricesParameters() + ..owner = owner + ..pageSize = 1, + uriHelper: ProductQuery.uriProductHelper, + ); + if (result.isError) { + return null; + } + return result.value.total; + } + UserPreferencesItem _buildProductQueryTile({ required final PagedProductQuery productQuery, required final String title, diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_connect.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_connect.dart index 8704baf50fa..1e4e0f169f5 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_connect.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_connect.dart @@ -111,7 +111,7 @@ class UserPreferencesConnect extends AbstractUserPreferences { 'assets/preferences/x-logo.svg', width: DEFAULT_ICON_SIZE, colorFilter: ui.ColorFilter.mode( - Theme.of(context).colorScheme.onBackground, + Theme.of(context).colorScheme.onSurface, ui.BlendMode.srcIn, ), package: AppHelper.APP_PACKAGE, diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_contribute.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_contribute.dart index b84d1f9ea1a..530c2e94062 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_contribute.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_contribute.dart @@ -88,7 +88,7 @@ class UserPreferencesContribute extends AbstractUserPreferences { _getListTile( appLocalizations.contribute_join_skill_pool, () async => LaunchUrlHelper.launchURL( - 'https://docs.google.com/forms/d/e/1FAIpQLSfGHAn5KxW7ko3_GlDfQpVGKpPAMHMbDvY2IjtxfJSXxKJQ2A/viewform?usp=sf_link', + 'https://connect.openfoodfacts.org/join-the-contributor-skill-pool-open-food-facts', ), Icons.group, externalLink: true, diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_country_selector.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_country_selector.dart index e58dfa3cc0e..6db20531a04 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_country_selector.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_country_selector.dart @@ -34,6 +34,7 @@ class UserPreferencesCountrySelector extends StatelessWidget { bottom: SMALL_SPACE, ), child: CountrySelector( + forceCurrencyChange: false, textStyle: themeData.textTheme.bodyMedium, icon: const Icon(Icons.edit), padding: const EdgeInsetsDirectional.only( diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_currency_selector.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_currency_selector.dart new file mode 100644 index 00000000000..dcf755977b9 --- /dev/null +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_currency_selector.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/onboarding/currency_selector.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; + +/// Currency selector within user preferences. +class UserPreferencesCurrencySelector extends StatelessWidget { + const UserPreferencesCurrencySelector(); + + static UserPreferencesItem getUserPreferencesItem( + final BuildContext context, + ) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return UserPreferencesItemSimple( + labels: [_getLabel(appLocalizations)], + builder: (_) => const UserPreferencesCurrencySelector(), + ); + } + + static String _getLabel(final AppLocalizations appLocalizations) => + appLocalizations.currency_chooser_label; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final ThemeData themeData = Theme.of(context); + return ListTile( + title: Text( + _getLabel(appLocalizations), + style: themeData.textTheme.headlineMedium, + ), + subtitle: Padding( + padding: const EdgeInsetsDirectional.only( + top: SMALL_SPACE, + bottom: SMALL_SPACE, + ), + child: CurrencySelector( + textStyle: themeData.textTheme.bodyMedium, + icon: const Icon(Icons.edit), + padding: const EdgeInsetsDirectional.only( + start: SMALL_SPACE, + ), + ), + ), + minVerticalPadding: MEDIUM_SPACE, + ); + } +} diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart index dce7776ffcf..f14ae46d4f6 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart @@ -2,17 +2,23 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/background/background_task_badge.dart'; import 'package:smooth_app/background/background_task_language_refresh.dart'; import 'package:smooth_app/data_models/continuous_scan_model.dart'; +import 'package:smooth_app/data_models/news_feed/newsfeed_provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/product_list.dart'; +import 'package:smooth_app/database/dao_osm_location.dart'; import 'package:smooth_app/database/dao_product.dart'; import 'package:smooth_app/database/dao_product_list.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:smooth_app/pages/locations/search_location_helper.dart'; +import 'package:smooth_app/pages/locations/search_location_preloaded_item.dart'; import 'package:smooth_app/pages/offline_data_page.dart'; import 'package:smooth_app/pages/offline_tasks_page.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; @@ -21,6 +27,7 @@ import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_page.dart'; import 'package:smooth_app/pages/preferences/user_preferences_search_page.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; +import 'package:smooth_app/pages/scan/search_page.dart'; import 'package:smooth_app/query/product_query.dart'; /// Full page display of "dev mode" for the preferences page. @@ -52,8 +59,9 @@ class UserPreferencesDevMode extends AbstractUserPreferences { static const String userPreferencesFlagAccessibilityEmoji = '__accessibilityEmoji'; static const String userPreferencesFlagUserOrderedKP = '__userOrderedKP'; - static const String userPreferencesFlagShortcutToPrices = - '__shortcutToPrices'; + static const String userPreferencesFlagSpellCheckerOnOcr = + '__spellcheckerOcr'; + static const String userPreferencesCustomNewsJSONURI = '__newsJsonURI'; final TextEditingController _textFieldController = TextEditingController(); @@ -95,74 +103,35 @@ class UserPreferencesDevMode extends AbstractUserPreferences { value: userPreferences.devMode == 1, ), UserPreferencesItemTile( - title: appLocalizations.dev_preferences_reset_onboarding_title, - subtitle: appLocalizations.dev_preferences_reset_onboarding_subtitle, - onTap: () async { - await userPreferences.resetOnboarding(); - _showSuccessMessage(); - }, + title: 'Debugging information', + onTap: () async => Navigator.of(context).push(MaterialPageRoute( + builder: (BuildContext context) => + const UserPreferencesDebugInfo())), + ), + UserPreferencesItemSection( + label: appLocalizations.dev_mode_section_data, ), UserPreferencesItemTile( - title: appLocalizations.dev_preferences_environment_switch_title, - trailing: DropdownButton( - value: userPreferences.getFlag(userPreferencesFlagProd) ?? true, - elevation: 16, - onChanged: (bool? newValue) async { - await userPreferences.setFlag(userPreferencesFlagProd, newValue); - ProductQuery.setQueryType(userPreferences); - }, - items: const >[ - DropdownMenuItem( - value: true, - child: Text('PROD'), - ), - DropdownMenuItem( - value: false, - child: Text('TEST'), - ), - ], + title: appLocalizations.background_task_title, + subtitle: appLocalizations.background_task_subtitle, + trailing: const BackgroundTaskBadge( + child: Icon(Icons.edit_notifications_outlined), + ), + onTap: () async => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => const OfflineTaskPage(), + ), ), ), UserPreferencesItemTile( - title: appLocalizations.dev_preferences_test_environment_title, - subtitle: appLocalizations.dev_preferences_test_environment_subtitle( - ProductQuery.getTestUriProductHelper(userPreferences) - .getPostUri(path: '') - .toString(), + title: appLocalizations.offline_data, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => const OfflineDataPage(), + ), ), - onTap: () async => _changeTestEnvDomain(), - ), - UserPreferencesItemSwitch( - title: appLocalizations.dev_preferences_edit_ingredients_title, - value: userPreferences.getFlag(userPreferencesFlagEditIngredients) ?? - false, - onChanged: (bool value) async { - await userPreferences.setFlag( - userPreferencesFlagEditIngredients, value); - _showSuccessMessage(); - }, - ), - UserPreferencesItemSwitch( - title: 'Accessibility: remove colors', - value: userPreferences - .getFlag(userPreferencesFlagAccessibilityNoColor) ?? - false, - onChanged: (bool value) async { - await userPreferences.setFlag( - userPreferencesFlagAccessibilityNoColor, value); - _showSuccessMessage(); - }, - ), - UserPreferencesItemSwitch( - title: 'Accessibility: show emoji', - value: - userPreferences.getFlag(userPreferencesFlagAccessibilityEmoji) ?? - false, - onChanged: (bool value) async { - await userPreferences.setFlag( - userPreferencesFlagAccessibilityEmoji, value); - _showSuccessMessage(); - }, ), UserPreferencesItemTile( title: appLocalizations.dev_preferences_export_history_title, @@ -232,26 +201,23 @@ class UserPreferencesDevMode extends AbstractUserPreferences { }, ), UserPreferencesItemTile( - title: appLocalizations.offline_data, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => const OfflineDataPage(), - ), - ), + title: 'Refresh all products from server (cf. Nutriscore v2)', + trailing: const Icon(Icons.refresh), + onTap: () async { + final LocalDatabase localDatabase = context.read(); + final DaoProduct daoProduct = DaoProduct(localDatabase); + await daoProduct.clearAllLanguages(); + await BackgroundTaskLanguageRefresh.addTask(localDatabase); + _showSuccessMessage(); + }, ), UserPreferencesItemTile( - title: appLocalizations.background_task_title, - subtitle: appLocalizations.background_task_subtitle, - trailing: const BackgroundTaskBadge( - child: Icon(Icons.edit_notifications_outlined), - ), - onTap: () async => Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => const OfflineTaskPage(), - ), - ), + // Do not translate + title: 'Reset app language', + onTap: () async { + userPreferences.setAppLanguageCode(null); + ProductQuery.setLanguage(context, userPreferences); + }, ), UserPreferencesItemTile( title: 'Add cards to scanner', @@ -270,6 +236,95 @@ class UserPreferencesDevMode extends AbstractUserPreferences { } }, ), + UserPreferencesItemSection( + label: appLocalizations.dev_mode_section_server, + ), + UserPreferencesItemTile( + title: appLocalizations.dev_preferences_environment_switch_title, + trailing: DropdownButton( + value: userPreferences.getFlag(userPreferencesFlagProd) ?? true, + elevation: 16, + onChanged: (bool? newValue) async { + await userPreferences.setFlag(userPreferencesFlagProd, newValue); + ProductQuery.setQueryType(userPreferences); + }, + items: const >[ + DropdownMenuItem( + value: true, + child: Text('PROD'), + ), + DropdownMenuItem( + value: false, + child: Text('TEST'), + ), + ], + ), + ), + UserPreferencesItemTile( + title: appLocalizations.dev_preferences_test_environment_title, + subtitle: appLocalizations.dev_preferences_test_environment_subtitle( + ProductQuery.getTestUriProductHelper(userPreferences) + .getPostUri(path: '') + .toString(), + ), + onTap: () async => _changeTestEnvDomain(), + ), + UserPreferencesItemSection( + label: appLocalizations.dev_mode_section_news, + ), + UserPreferencesEditableItemTile( + title: appLocalizations.dev_preferences_news_custom_url_title, + subtitleWithEmptyValue: + appLocalizations.dev_preferences_news_custom_url_empty_value, + dialogAction: + appLocalizations.dev_preferences_news_custom_url_subtitle, + value: userPreferences + .getDevModeString(userPreferencesCustomNewsJSONURI), + onNewValue: (String newUrl) => userPreferences.setDevModeString( + userPreferencesCustomNewsJSONURI, + newUrl, + ), + validator: (String value) => + value.isEmpty || Uri.tryParse(value) != null, + ), + UserPreferencesItemTileBuilder( + title: appLocalizations.dev_preferences_news_provider_status_title, + subtitleBuilder: (BuildContext context) { + return Consumer( + builder: (_, AppNewsProvider provider, __) { + return Text(switch (provider.state) { + AppNewsStateLoading() => 'Loading...', + AppNewsStateLoaded(lastUpdate: final DateTime date) => + appLocalizations + .dev_preferences_news_provider_status_subtitle( + DateFormat.yMd().format(date), + ), + AppNewsStateError(exception: final dynamic e) => 'Error $e', + }); + }); + }, + trailingBuilder: (BuildContext context) { + return IconButton( + icon: const Icon(Icons.refresh), + onPressed: () => context + .read() + .loadLatestNews(forceUpdate: true), + ); + }, + ), + UserPreferencesItemSection( + label: appLocalizations.dev_mode_section_product_page, + ), + UserPreferencesItemSwitch( + title: appLocalizations.dev_preferences_edit_ingredients_title, + value: userPreferences.getFlag(userPreferencesFlagEditIngredients) ?? + false, + onChanged: (bool value) async { + await userPreferences.setFlag( + userPreferencesFlagEditIngredients, value); + _showSuccessMessage(); + }, + ), UserPreferencesItemSwitch( title: appLocalizations.dev_mode_hide_ecoscore_title, value: userPreferences @@ -285,41 +340,52 @@ class UserPreferencesDevMode extends AbstractUserPreferences { await userPreferences.setExcludedAttributeIds(list); }, ), + UserPreferencesItemSection( + label: appLocalizations.dev_mode_section_ui, + ), UserPreferencesItemTile( - // Do not translate - title: 'Reset app language', + title: appLocalizations.dev_preferences_reset_onboarding_title, + subtitle: appLocalizations.dev_preferences_reset_onboarding_subtitle, onTap: () async { - userPreferences.setAppLanguageCode(null); - ProductQuery.setLanguage(context, userPreferences); + await userPreferences.resetOnboarding(); + _showSuccessMessage(); }, ), - UserPreferencesItemTile( - title: 'Refresh all products from server (cf. Nutriscore v2)', - trailing: const Icon(Icons.refresh), - onTap: () async { - final LocalDatabase localDatabase = context.read(); - final DaoProduct daoProduct = DaoProduct(localDatabase); - await daoProduct.clearAllLanguages(); - await BackgroundTaskLanguageRefresh.addTask(localDatabase); + UserPreferencesItemSwitch( + title: 'Accessibility: remove colors', + value: userPreferences + .getFlag(userPreferencesFlagAccessibilityNoColor) ?? + false, + onChanged: (bool value) async { + await userPreferences.setFlag( + userPreferencesFlagAccessibilityNoColor, value); _showSuccessMessage(); }, ), UserPreferencesItemSwitch( - title: 'Side by side comparison for 2 or 3 products', + title: 'Accessibility: show emoji', value: - userPreferences.getFlag(userPreferencesFlagBoostedComparison) ?? + userPreferences.getFlag(userPreferencesFlagAccessibilityEmoji) ?? false, onChanged: (bool value) async { await userPreferences.setFlag( - userPreferencesFlagBoostedComparison, value); + userPreferencesFlagAccessibilityEmoji, value); _showSuccessMessage(); }, ), - UserPreferencesItemTile( - title: 'Debugging information', - onTap: () async => Navigator.of(context).push(MaterialPageRoute( - builder: (BuildContext context) => - const UserPreferencesDebugInfo())), + UserPreferencesItemSwitch( + title: appLocalizations.dev_mode_spellchecker_for_ocr_title, + subtitle: appLocalizations.dev_mode_spellchecker_for_ocr_subtitle, + value: + userPreferences.getFlag(userPreferencesFlagSpellCheckerOnOcr) ?? + false, + onChanged: (bool value) async => userPreferences.setFlag( + userPreferencesFlagSpellCheckerOnOcr, + value, + ), + ), + UserPreferencesItemSection( + label: appLocalizations.dev_mode_section_experimental_features, ), UserPreferencesItemSwitch( title: 'User ordered knowledge panels', @@ -331,14 +397,52 @@ class UserPreferencesDevMode extends AbstractUserPreferences { _showSuccessMessage(); }, ), - UserPreferencesItemSwitch( - title: appLocalizations.prices_app_dev_mode_flag, - value: userPreferences.getFlag(userPreferencesFlagShortcutToPrices) ?? - false, - onChanged: (bool value) async { - await userPreferences.setFlag( - userPreferencesFlagShortcutToPrices, value); - _showSuccessMessage(); + UserPreferencesItemTile( + title: 'Temporary access to location search', + onTap: () async { + final LocalDatabase localDatabase = context.read(); + final DaoOsmLocation daoOsmLocation = DaoOsmLocation(localDatabase); + final List osmLocations = + await daoOsmLocation.getAll(); + if (!context.mounted) { + return; + } + final List preloadedList = + []; + for (final OsmLocation osmLocation in osmLocations) { + preloadedList.add( + SearchLocationPreloadedItem( + osmLocation, + popFirst: false, + ), + ); + } + final OsmLocation? osmLocation = await Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => SearchPage( + const SearchLocationHelper(), + preloadedList: preloadedList, + autofocus: false, + ), + ), + ); + if (osmLocation == null) { + return; + } + await daoOsmLocation.put(osmLocation); + if (!context.mounted) { + return; + } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + osmLocation.getTitle() ?? + osmLocation.getSubtitle() ?? + osmLocation.getLatLng().toString(), + ), + ), + ); }, ), UserPreferencesItemTile( @@ -350,6 +454,17 @@ class UserPreferencesDevMode extends AbstractUserPreferences { ), ), ), + UserPreferencesItemSwitch( + title: 'Side by side comparison for 2 or 3 products', + value: + userPreferences.getFlag(userPreferencesFlagBoostedComparison) ?? + false, + onChanged: (bool value) async { + await userPreferences.setFlag( + userPreferencesFlagBoostedComparison, value); + _showSuccessMessage(); + }, + ), ]; ScaffoldFeatureController diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_faq.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_faq.dart index b91e165e2e7..7263e39e9e2 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_faq.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_faq.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:smooth_app/cards/category_cards/svg_cache.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; @@ -11,6 +12,7 @@ import 'package:smooth_app/helpers/app_helper.dart'; import 'package:smooth_app/helpers/global_vars.dart'; import 'package:smooth_app/helpers/launch_url_helper.dart'; import 'package:smooth_app/helpers/user_feedback_helper.dart'; +import 'package:smooth_app/pages/guides/guide/guide_nutriscore_v2.dart'; import 'package:smooth_app/pages/preferences/abstract_user_preferences.dart'; import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart'; @@ -58,8 +60,30 @@ class UserPreferencesFaq extends AbstractUserPreferences { _getNutriListTile( title: appLocalizations.nutriscore_generic, url: 'https://world.openfoodfacts.org/nutriscore', - svg: 'assets/cache/nutriscore-b.svg', + svg: SvgCache.getAssetsCacheForNutriscore( + NutriScoreValue.b, + false, + ), ), + if (userPreferences.userCountryCode != 'fr') + _getListTile( + title: appLocalizations.faq_nutriscore_nutriscore, + leadingSvg: SvgCache.getAssetsCacheForNutriscore( + NutriScoreValue.b, + true, + ), + onTap: () => Navigator.of(context, rootNavigator: true).push( + MaterialPageRoute( + builder: (BuildContext context) => const GuideNutriscoreV2(), + ), + ), + + /// Hide the icon + icon: const Icon( + Icons.info, + size: 0.0, + ), + ), _getNutriListTile( title: appLocalizations.ecoscore_generic, url: 'https://world.openfoodfacts.org/ecoscore', @@ -222,7 +246,7 @@ class UserPreferencesFaq extends AbstractUserPreferences { children: [ SvgPicture.asset( logo, - width: MediaQuery.of(context).size.width * 0.1, + width: MediaQuery.sizeOf(context).width * 0.1, package: AppHelper.APP_PACKAGE, ), const SizedBox(width: SMALL_SPACE), @@ -303,7 +327,7 @@ class UserPreferencesFaq extends AbstractUserPreferences { applicationVersion: packageInfo.version, applicationIcon: SvgPicture.asset( logo, - height: MediaQuery.of(context).size.height * 0.1, + height: MediaQuery.sizeOf(context).height * 0.1, ), ), label: appLocalizations.licenses, diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_item.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_item.dart index f7f3f84497c..2e4b90c512d 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_item.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_item.dart @@ -17,5 +17,5 @@ class UserPreferencesItemSimple implements UserPreferencesItem { final Iterable labels; @override - final Widget Function(BuildContext) builder; + final WidgetBuilder builder; } diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_language_selector.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_language_selector.dart index 5509417f1a2..6882d43bdbf 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_language_selector.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_language_selector.dart @@ -3,6 +3,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; import 'package:smooth_app/background/background_task_language_refresh.dart'; +import 'package:smooth_app/data_models/news_feed/newsfeed_provider.dart'; import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/database/local_database.dart'; @@ -55,6 +56,11 @@ class UserPreferencesLanguageSelector extends StatelessWidget { await BackgroundTaskLanguageRefresh.addTask( context.read(), ); + + // Refresh the news feed + if (context.mounted) { + context.read().loadLatestNews(); + } // TODO(monsieurtanuki): make it a background task also? // no await productPreferences.refresh(); diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart index a4b7dd72f26..1aae09487a3 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_page.dart @@ -228,7 +228,7 @@ class _UserPreferencesPageState extends State ); } final bool dark = Theme.of(context).brightness == Brightness.dark; - final double backgroundHeight = MediaQuery.of(context).size.height * .20; + final double backgroundHeight = MediaQuery.sizeOf(context).height * .20; children.insert( 0, Container( diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_rate_us.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_rate_us.dart index cba7fc5eefe..3600c1330ab 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_rate_us.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_rate_us.dart @@ -50,10 +50,10 @@ class UserPreferencesRateUs extends StatelessWidget { content: Text( appLocalizations.error_occurred, textAlign: TextAlign.center, - style: TextStyle(color: themeData.colorScheme.background), + style: TextStyle(color: themeData.colorScheme.surface), ), behavior: SnackBarBehavior.floating, - backgroundColor: themeData.colorScheme.onBackground, + backgroundColor: themeData.colorScheme.onSurface, ), ); } diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_settings.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_settings.dart index 937cdc94908..8488f03e269 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_settings.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_settings.dart @@ -11,6 +11,7 @@ import 'package:smooth_app/pages/preferences/user_preferences_choose_accent_colo import 'package:smooth_app/pages/preferences/user_preferences_choose_app_theme.dart'; import 'package:smooth_app/pages/preferences/user_preferences_choose_text_color_contrast.dart'; import 'package:smooth_app/pages/preferences/user_preferences_country_selector.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_currency_selector.dart'; import 'package:smooth_app/pages/preferences/user_preferences_image_source.dart'; import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; import 'package:smooth_app/pages/preferences/user_preferences_language_selector.dart'; @@ -66,6 +67,8 @@ class UserPreferencesSettings extends AbstractUserPreferences { _getDivider(), UserPreferencesCountrySelector.getUserPreferencesItem(context), _getDivider(), + UserPreferencesCurrencySelector.getUserPreferencesItem(context), + _getDivider(), UserPreferencesLanguageSelector.getUserPreferencesItem(context), _getDivider(), UserPreferencesImageSource.getUserPreferencesItem(context), diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_share_with_friends.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_share_with_friends.dart index 6e9875ff5c3..44840fa1c28 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_share_with_friends.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_share_with_friends.dart @@ -44,11 +44,11 @@ class UserPreferencesShareWithFriends extends StatelessWidget { appLocalizations.error, textAlign: TextAlign.center, style: TextStyle( - color: themeData.colorScheme.background, + color: themeData.colorScheme.surface, ), ), behavior: SnackBarBehavior.floating, - backgroundColor: themeData.colorScheme.onBackground, + backgroundColor: themeData.colorScheme.onSurface, ), ); } diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_widgets.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_widgets.dart index 76390f6fdb1..735c70731a4 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_widgets.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_widgets.dart @@ -1,7 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; import 'package:smooth_app/generic_lib/bottom_sheets/smooth_bottom_sheet.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; import 'package:smooth_app/pages/preferences/user_preferences_item.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; /// A dashed line class UserPreferencesListItemDivider extends StatelessWidget { @@ -123,7 +127,7 @@ class UserPreferencesItemSwitch implements UserPreferencesItem { ]; @override - Widget Function(BuildContext) get builder => + WidgetBuilder get builder => (final BuildContext context) => UserPreferencesSwitchWidget( title: title, subtitle: subtitle, @@ -154,14 +158,88 @@ class UserPreferencesItemTile implements UserPreferencesItem { ]; @override - Widget Function(BuildContext) get builder => - (final BuildContext context) => ListTile( - title: Text(title), - subtitle: subtitle == null ? null : Text(subtitle!), - onTap: onTap, - leading: leading, - trailing: trailing, - ); + WidgetBuilder get builder => (final BuildContext context) => ListTile( + title: Text(title), + subtitle: subtitle == null ? null : Text(subtitle!), + onTap: onTap, + leading: leading, + trailing: trailing, + ); +} + +/// Same as [UserPreferencesItemTile] but with [WidgetBuilder]. +class UserPreferencesItemTileBuilder implements UserPreferencesItem { + const UserPreferencesItemTileBuilder({ + required this.title, + required this.subtitleBuilder, + this.onTap, + this.leadingBuilder, + this.trailingBuilder, + }); + + final String title; + final WidgetBuilder subtitleBuilder; + final VoidCallback? onTap; + final WidgetBuilder? leadingBuilder; + final WidgetBuilder? trailingBuilder; + + @override + List get labels => [title]; + + @override + WidgetBuilder get builder => (final BuildContext context) => ListTile( + title: Text(title), + subtitle: subtitleBuilder.call(context), + onTap: onTap, + leading: leadingBuilder?.call(context), + trailing: trailingBuilder?.call(context), + ); +} + +class UserPreferencesItemSection implements UserPreferencesItem { + const UserPreferencesItemSection({ + required this.label, + this.icon, + }) : assert(label.length > 0); + + final String label; + final Widget? icon; + + @override + WidgetBuilder get builder => (BuildContext context) { + final SmoothColorsThemeExtension colors = + Theme.of(context).extension()!; + + return Container( + color: colors.primaryDark, + padding: const EdgeInsets.symmetric( + horizontal: LARGE_SPACE, + vertical: SMALL_SPACE, + ), + child: Row( + children: [ + Expanded( + child: Text( + label, + style: TextStyle( + color: colors.primaryLight, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ), + if (icon != null) + IconTheme( + data: IconThemeData(color: colors.primaryLight), + child: icon!, + ), + ], + ), + ); + }; + + @override + Iterable get labels => [label]; } /// A preference allowing to choose between a list of items. @@ -449,3 +527,128 @@ class UserPreferenceListTile extends StatelessWidget { ); } } + +class UserPreferencesEditableItemTile extends UserPreferencesItemTile { + const UserPreferencesEditableItemTile({ + required super.title, + required String dialogAction, + required this.onNewValue, + this.subtitleWithEmptyValue, + this.validator, + this.hint, + this.value, + }) : assert(dialogAction.length > 0), + super(subtitle: dialogAction); + + final String? value; + final String? hint; + final String? subtitleWithEmptyValue; + final bool Function(String)? validator; + final Function(String) onNewValue; + + @override + WidgetBuilder get builder => (BuildContext context) { + return ListTile( + title: Text(title), + subtitle: Text(value?.isNotEmpty == true + ? value! + : (subtitleWithEmptyValue ?? '-')), + onTap: () async => _showInputTextDialog(context), + ); + }; + + Future _showInputTextDialog(BuildContext context) async { + final TextEditingController controller = + TextEditingController(text: value ?? ''); + + final dynamic res = await showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return ChangeNotifierProvider.value( + value: controller, + child: Consumer( + builder: + (BuildContext context, TextEditingController controller, _) { + return SmoothAlertDialog( + title: title, + close: true, + body: _UserPreferencesEditableDialogContent( + title: subtitle!, + hint: hint, + ), + positiveAction: SmoothActionButton( + text: appLocalizations.okay, + onPressed: validator?.call(controller.text) != false + ? () => Navigator.of(context).pop(controller.text) + : null, + ), + negativeAction: SmoothActionButton( + text: appLocalizations.cancel, + onPressed: () => Navigator.of(context).pop(), + ), + ); + }, + ), + ); + }, + ); + + if (res is String && res != value) { + onNewValue.call(res); + } + } +} + +class _UserPreferencesEditableDialogContent extends StatefulWidget { + const _UserPreferencesEditableDialogContent({ + required this.title, + this.hint, + }); + + final String title; + final String? hint; + + @override + State<_UserPreferencesEditableDialogContent> createState() => + _InputTextDialogBodyState(); +} + +class _InputTextDialogBodyState + extends State<_UserPreferencesEditableDialogContent> { + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.title), + const SizedBox(height: 10), + TextField( + controller: Provider.of(context), + autocorrect: false, + autofocus: true, + textInputAction: TextInputAction.send, + decoration: InputDecoration( + hintText: widget.hint, + suffix: Semantics( + button: true, + label: MaterialLocalizations.of(context).deleteButtonTooltip, + excludeSemantics: true, + child: InkWell( + onTap: () => context.read().clear(), + customBorder: const CircleBorder(), + child: const Padding( + padding: EdgeInsetsDirectional.all(SMALL_SPACE), + child: Icon(Icons.clear), + ), + ), + ), + ), + onSubmitted: (String value) => Navigator.of(context).pop(value), + ), + ], + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/currency_extension.dart b/packages/smooth_app/lib/pages/prices/currency_extension.dart new file mode 100644 index 00000000000..07e76c3ca93 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/currency_extension.dart @@ -0,0 +1,127 @@ +import 'package:openfoodfacts/openfoodfacts.dart'; + +/// Currency symbols. +extension CurrencyExtension on Currency { + String getFullName() { + final String? symbol = _symbols[this]; + if (symbol == null) { + return name; + } + // e.g. for CHF + if (symbol == name) { + return name; + } + return '$name ($symbol)'; + } + +// source: https://www.xe.com/symbols/ + static final Map _symbols = { + Currency.ALL: 'Lek', + Currency.AFN: String.fromCharCode(1547), + Currency.ARS: r'$', + Currency.AWG: String.fromCharCode(402), + Currency.AUD: r'$', + Currency.AZN: String.fromCharCode(8380), + Currency.BSD: r'$', + Currency.BBD: r'$', + Currency.BYN: 'Br', + Currency.BZD: r'BZ$', + Currency.BMD: r'$', + Currency.BOB: r'$b', + Currency.BAM: 'KM', + Currency.BWP: 'P', + Currency.BGN: '${String.fromCharCode(1083)}${String.fromCharCode(1074)}', + Currency.BRL: r'R$', + Currency.BND: r'$', + Currency.KHR: String.fromCharCode(6107), + Currency.CAD: r'$', + Currency.KYD: r'$', + Currency.CLP: r'$', + Currency.CNY: String.fromCharCode(165), + Currency.COP: r'$', + Currency.CRC: String.fromCharCode(8353), + Currency.HRK: 'kn', + Currency.CUP: String.fromCharCode(8369), + Currency.CZK: 'K${String.fromCharCode(269)}', + Currency.DKK: 'kr', + Currency.DOP: r'RD$', + Currency.XCD: r'$', + Currency.EGP: String.fromCharCode(163), + Currency.SVC: r'$', + Currency.EUR: String.fromCharCode(8364), + Currency.FKP: String.fromCharCode(163), + Currency.FJD: r'$', + Currency.GHS: String.fromCharCode(162), + Currency.GIP: String.fromCharCode(163), + Currency.GTQ: 'Q', + Currency.GYD: r'$', + Currency.HNL: 'L', + Currency.HKD: r'$', + Currency.HUF: 'Ft', + Currency.ISK: 'kr', + Currency.INR: String.fromCharCode(8377), + Currency.IDR: 'Rp', + Currency.IRR: String.fromCharCode(65020), + Currency.ILS: String.fromCharCode(8362), + Currency.JMD: r'J$', + Currency.JPY: String.fromCharCode(165), + Currency.KZT: '${String.fromCharCode(1083)}${String.fromCharCode(1074)}', + Currency.KPW: String.fromCharCode(8361), + Currency.KRW: String.fromCharCode(8361), + Currency.KGS: '${String.fromCharCode(1083)}${String.fromCharCode(1074)}', + Currency.LAK: String.fromCharCode(8365), + Currency.LBP: String.fromCharCode(163), + Currency.LRD: r'$', + Currency.MKD: + '${String.fromCharCode(1076)}${String.fromCharCode(1077)}${String.fromCharCode(1085)}', + Currency.MYR: 'RM', + Currency.MUR: String.fromCharCode(8360), + Currency.MXN: r'$', + Currency.MNT: String.fromCharCode(8366), + Currency.MZN: 'MT', + Currency.NAD: r'$', + Currency.NPR: String.fromCharCode(8360), + Currency.ANG: String.fromCharCode(402), + Currency.NZD: r'$', + Currency.NIO: r'C$', + Currency.NGN: String.fromCharCode(8358), + Currency.NOK: 'kr', + Currency.OMR: String.fromCharCode(65020), + Currency.PKR: String.fromCharCode(8360), + Currency.PAB: 'B/.', + Currency.PYG: 'Gs', + Currency.PEN: 'S/.', + Currency.PHP: String.fromCharCode(8369), + Currency.PLN: 'z${String.fromCharCode(322)}', + Currency.QAR: String.fromCharCode(65020), + Currency.RON: 'lei', + Currency.RUB: String.fromCharCode(8381), + Currency.SHP: String.fromCharCode(163), + Currency.SAR: String.fromCharCode(65020), + Currency.RSD: + '${String.fromCharCode(1044)}${String.fromCharCode(1080)}${String.fromCharCode(1085)}.', + Currency.SCR: String.fromCharCode(8360), + Currency.SGD: r'$', + Currency.SBD: r'$', + Currency.SOS: 'S', + Currency.ZAR: 'R', + Currency.LKR: String.fromCharCode(8360), + Currency.SEK: 'kr', + Currency.CHF: 'CHF', + Currency.SRD: r'$', + Currency.SYP: String.fromCharCode(163), + Currency.TWD: r'NT$', + Currency.THB: String.fromCharCode(3647), + Currency.TTD: r'TT$', + Currency.TRY: String.fromCharCode(8378), + Currency.UAH: String.fromCharCode(8372), + Currency.GBP: String.fromCharCode(163), + Currency.USD: r'$', + Currency.UYU: r'$U', + Currency.UZS: '${String.fromCharCode(1083)}${String.fromCharCode(1074)}', + Currency.VEF: 'Bs', + Currency.VND: String.fromCharCode(8363), + Currency.YER: String.fromCharCode(65020), + Currency.ZWD: r'Z$', + }; +} diff --git a/packages/smooth_app/lib/pages/prices/emoji_helper.dart b/packages/smooth_app/lib/pages/prices/emoji_helper.dart new file mode 100644 index 00000000000..77a545b1d9b --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/emoji_helper.dart @@ -0,0 +1,44 @@ +import 'package:openfoodfacts/openfoodfacts.dart'; + +/// Generic helper about emoji display. +class EmojiHelper { + /// Returns the country flag emoji. + /// + /// cf. https://emojipedia.org/flag-italy + String? getCountryEmoji(final OpenFoodFactsCountry? country) { + if (country == null) { + return null; + } + return _getCountryEmojiFromUnicode(country.offTag); + } + + static const int _emojiCountryLetterA = 0x1F1E6; + static const int _asciiCapitalA = 65; + static const int _asciiCapitalZ = 90; + + static String? _getCountryEmojiFromUnicode(final String unicode) { + final String? countryLetterEmoji1 = _getCountryLetterEmoji( + unicode.substring(0, 1), + ); + if (countryLetterEmoji1 == null) { + return null; + } + //OpenFoodFactsCountry + final String? countryLetterEmoji2 = _getCountryLetterEmoji( + unicode.substring(1, 2), + ); + if (countryLetterEmoji2 == null) { + return null; + } + return '$countryLetterEmoji1$countryLetterEmoji2'; + } + + static String? _getCountryLetterEmoji(final String letter) { + final int ascii = letter.toUpperCase().codeUnitAt(0); + if (ascii < _asciiCapitalA || ascii > _asciiCapitalZ) { + return null; + } + final int code = _emojiCountryLetterA + ascii - _asciiCapitalA; + return String.fromCharCode(code); + } +} diff --git a/packages/smooth_app/lib/pages/prices/eraser_model.dart b/packages/smooth_app/lib/pages/prices/eraser_model.dart new file mode 100644 index 00000000000..6a23783ae09 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/eraser_model.dart @@ -0,0 +1,130 @@ +import 'package:crop_image/crop_image.dart'; +import 'package:flutter/rendering.dart'; + +/// Model about the eraser tool: coordinate computations. +class EraserModel { + EraserModel({ + this.rotation = CropRotation.up, + final List? offsets, + }) : offsets = offsets ?? []; + + CropRotation rotation; + + final List offsets; + + Rect? cropRect; + + late double _imageWidth; + late double _imageHeight; + + // Canvas size. + set size(final Size value) { + _imageWidth = value.width; + _imageHeight = value.height; + } + + // Full displayed image dimensions. For screen crop grid only. + late double _fullWidth; + late double _fullHeight; + + set _boxConstraints(final BoxConstraints value) { + _fullWidth = value.maxWidth; + _fullHeight = value.maxHeight; + } + + double get _deltaX => (_fullWidth - _imageWidth) / 2; + double get _deltaY => (_fullHeight - _imageHeight) / 2; + + Offset? _latestStart; + Offset? _latestUpdate; + + bool get isEmpty => offsets.isEmpty; + + int get length => offsets.length ~/ 2; + + static const Rect _fullImageCropRect = Rect.fromLTRB(0, 0, 1, 1); + + // From full image [0,1] to possibly cropped + Offset _fromPct(final Offset offset) { + final Rect rect = cropRect ?? _fullImageCropRect; + return switch (rotation) { + CropRotation.down => Offset( + (1 - offset.dx - rect.left) / rect.width * _imageWidth, + (1 - offset.dy - rect.top) / rect.height * _imageHeight, + ), + CropRotation.left => Offset( + (offset.dy - rect.left) / rect.width * _imageWidth, + (1 - offset.dx - rect.top) / rect.height * _imageHeight, + ), + CropRotation.right => Offset( + (1 - offset.dy - rect.left) / rect.width * _imageWidth, + (offset.dx - rect.top) / rect.height * _imageHeight, + ), + CropRotation.up => Offset( + (offset.dx - rect.left) / rect.width * _imageWidth, + (offset.dy - rect.top) / rect.height * _imageHeight, + ), + }; + } + + // From screen offset to full image [0,1] offset + Offset _toPct(final Offset offset) => switch (rotation) { + CropRotation.down => Offset( + (_imageWidth - (offset.dx - _deltaX)) / _imageWidth, + (_imageHeight - (offset.dy - _deltaY)) / _imageHeight, + ), + CropRotation.left => Offset( + (_imageHeight - (offset.dy - _deltaY)) / _imageHeight, + (0 + (offset.dx - _deltaX)) / _imageWidth, + ), + CropRotation.right => Offset( + (offset.dy - _deltaY) / _imageHeight, + (_imageWidth - (offset.dx - _deltaX)) / _imageWidth, + ), + _ => Offset( + (offset.dx - _deltaX) / _imageWidth, + (offset.dy - _deltaY) / _imageHeight, + ), + }; + + Offset getStart(final int index) => _fromPct(offsets[2 * index]); + + Offset getEnd(final int index) => _fromPct(offsets[2 * index + 1]); + + Offset? getCurrentStart() => + _latestStart == null ? null : _fromPct(_latestStart!); + + Offset? getCurrentEnd() => + _latestUpdate == null ? null : _fromPct(_latestUpdate!); + + void panStart( + final Offset offset, + final BoxConstraints constraints, + ) { + _boxConstraints = constraints; + _latestStart = _latestUpdate = _toPct(offset); + } + + void panUpdate( + final Offset offset, + final BoxConstraints constraints, + ) { + _boxConstraints = constraints; + _latestUpdate = _toPct(offset); + } + + void panEnd() { + if (_latestStart != _latestUpdate) { + if (_latestStart != null && _latestUpdate != null) { + offsets.add(_latestStart!); + offsets.add(_latestUpdate!); + } + } + _latestStart = _latestUpdate = null; + } + + void undo() { + offsets.removeLast(); + offsets.removeLast(); + } +} diff --git a/packages/smooth_app/lib/pages/prices/eraser_painter.dart b/packages/smooth_app/lib/pages/prices/eraser_painter.dart new file mode 100644 index 00000000000..86e2715fb18 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/eraser_painter.dart @@ -0,0 +1,62 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:smooth_app/pages/prices/eraser_model.dart'; + +/// Painter of the eraser tool: displaying thick lines. +class EraserPainter extends CustomPainter { + EraserPainter({ + required this.eraserModel, + this.cropRect, + }); + + final EraserModel eraserModel; + final Rect? cropRect; + + static const Color color = Colors.black; + + final Paint _paint = Paint() + ..color = color + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + final Path _path = Path(); + + void _addToPath(final Offset start, final Offset end) { + _path.moveTo(start.dx, start.dy); + _path.lineTo(end.dx, end.dy); + } + + static const double _strokeWidthFactor = .03; + + @override + void paint(Canvas canvas, Size size) { + eraserModel.size = size; + + eraserModel.cropRect = cropRect; + + if (cropRect == null) { + _paint.strokeWidth = _strokeWidthFactor * sqrt(size.width * size.height); + } else { + _paint.strokeWidth = _strokeWidthFactor * + sqrt(size.width * size.height / cropRect!.width / cropRect!.height); + } + + _path.reset(); + for (int i = 0; i < eraserModel.length; i++) { + _addToPath(eraserModel.getStart(i), eraserModel.getEnd(i)); + } + final Offset? currentStart = eraserModel.getCurrentStart(); + final Offset? currentEnd = eraserModel.getCurrentEnd(); + if (currentStart != null && currentEnd != null) { + _addToPath(currentStart, currentEnd); + } + + eraserModel.cropRect = null; + + canvas.drawPath(_path, _paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} diff --git a/packages/smooth_app/lib/pages/prices/get_prices_model.dart b/packages/smooth_app/lib/pages/prices/get_prices_model.dart new file mode 100644 index 00000000000..2087975356d --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/get_prices_model.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; +import 'package:smooth_app/pages/prices/product_price_add_page.dart'; +import 'package:smooth_app/query/product_query.dart'; + +/// Model that stores what we need to know for "get latest prices" queries. +class GetPricesModel { + const GetPricesModel({ + required this.parameters, + required this.displayOwner, + required this.displayProduct, + required this.uri, + required this.title, + this.enableCountButton = true, + this.subtitle, + this.addButton, + }); + + /// Gets latest prices for a product. + factory GetPricesModel.product({ + required final PriceMetaProduct product, + required final BuildContext context, + }) => + GetPricesModel( + parameters: _getProductPricesParameters(product.barcode), + displayOwner: true, + displayProduct: false, + uri: _getProductPricesUri(product.barcode), + title: product.getName(AppLocalizations.of(context)), + subtitle: product.barcode, + addButton: () async => ProductPriceAddPage.showProductPage( + context: context, + product: product, + proofType: ProofType.priceTag, + ), + enableCountButton: false, + ); + + static GetPricesParameters _getProductPricesParameters( + final String barcode, + ) => + GetPricesParameters() + ..productCode = barcode + ..orderBy = >[ + const OrderBy( + field: GetPricesOrderField.created, + ascending: false, + ), + ] + ..pageSize = pageSize + ..pageNumber = 1; + + static Uri _getProductPricesUri( + final String barcode, + ) => + OpenPricesAPIClient.getUri( + path: 'app/products/$barcode', + uriHelper: ProductQuery.uriProductHelper, + ); + + /// Query parameters. + final GetPricesParameters parameters; + + /// Should we display the owner for each price? No if it's an owner query. + final bool displayOwner; + + /// Should we display the product for each price? No if it's a product query. + final bool displayProduct; + + /// Related web app URI. + final Uri uri; + + /// Page title. + final String title; + + /// Page subtitle. + final String? subtitle; + + /// "Add a price" callback. + final VoidCallback? addButton; + + /// "Enable the count button?". Typically "false" for product price pages. + final bool enableCountButton; + + static const int pageSize = 10; +} diff --git a/packages/smooth_app/lib/pages/prices/price_amount_card.dart b/packages/smooth_app/lib/pages/prices/price_amount_card.dart new file mode 100644 index 00000000000..314c5209045 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_amount_card.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.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/prices/price_amount_field.dart'; +import 'package:smooth_app/pages/prices/price_amount_model.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; +import 'package:smooth_app/pages/prices/price_model.dart'; +import 'package:smooth_app/pages/prices/price_product_list_tile.dart'; +import 'package:smooth_app/pages/prices/price_product_search_page.dart'; + +/// Card that displays the amounts (discounted or not) for price adding. +class PriceAmountCard extends StatefulWidget { + PriceAmountCard({ + required this.priceModel, + required this.index, + required this.refresh, + }) : model = priceModel.priceAmountModels[index], + total = priceModel.priceAmountModels.length; + + final PriceModel priceModel; + final PriceAmountModel model; + final int index; + final int total; + // TODO(monsieurtanuki): not elegant, the display was not refreshed when removing an item + final VoidCallback refresh; + + @override + State createState() => _PriceAmountCardState(); +} + +class _PriceAmountCardState extends State { + final TextEditingController _controllerPaid = TextEditingController(); + final TextEditingController _controllerWithoutDiscount = + TextEditingController(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final bool isEmpty = widget.model.product.barcode.isEmpty; + return SmoothCard( + child: Column( + children: [ + Text( + '${appLocalizations.prices_amount_subtitle}' + '${widget.total == 1 ? '' : ' (${widget.index + 1}/${widget.total})'}', + ), + PriceProductListTile( + product: widget.model.product, + trailingIconData: isEmpty + ? Icons.edit + : widget.total == 1 + ? null + : Icons.clear, + onPressed: isEmpty + ? () async { + final PriceMetaProduct? product = + await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => + const PriceProductSearchPage(), + ), + ); + if (product == null) { + return; + } + setState(() => widget.model.product = product); + } + : widget.total == 1 + ? null + : () { + widget.priceModel.priceAmountModels + .removeAt(widget.index); + widget.refresh.call(); + }, + ), + SmoothLargeButtonWithIcon( + icon: widget.model.promo + ? Icons.check_box + : Icons.check_box_outline_blank, + text: appLocalizations.prices_amount_is_discounted, + onPressed: () => setState( + () => widget.model.promo = !widget.model.promo, + ), + ), + const SizedBox(height: SMALL_SPACE), + Row( + children: [ + Expanded( + child: PriceAmountField( + controller: _controllerPaid, + isPaidPrice: true, + model: widget.model, + ), + ), + const SizedBox(width: LARGE_SPACE), + Expanded( + child: !widget.model.promo + ? Container() + : PriceAmountField( + controller: _controllerWithoutDiscount, + isPaidPrice: false, + model: widget.model, + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_amount_field.dart b/packages/smooth_app/lib/pages/prices/price_amount_field.dart new file mode 100644 index 00000000000..a45093b5014 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_amount_field.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_text_form_field.dart'; +import 'package:smooth_app/pages/prices/price_amount_model.dart'; + +/// Text field that displays a single amount for price adding. +class PriceAmountField extends StatelessWidget { + const PriceAmountField({ + required this.model, + required this.isPaidPrice, + required this.controller, + }); + + final PriceAmountModel model; + final bool isPaidPrice; + final TextEditingController controller; + + // TODO(monsieurtanuki): TextInputAction + focus + static const TextInputType _priceTextInputType = + TextInputType.numberWithOptions( + signed: false, + decimal: true, + ); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothTextFormField( + type: TextFieldTypes.PLAIN_TEXT, + controller: controller, + hintText: !isPaidPrice + ? appLocalizations.prices_amount_price_not_discounted + : model.promo + ? appLocalizations.prices_amount_price_discounted + : appLocalizations.prices_amount_price_normal, + textInputType: _priceTextInputType, + onChanged: (final String? value) { + if (isPaidPrice) { + model.paidPrice = value ?? ''; + return; + } + model.priceWithoutDiscount = value ?? ''; + }, + validator: (String? value) { + if (isPaidPrice) { + if (value == null || value.isEmpty) { + return appLocalizations.prices_amount_price_mandatory; + } + final double? doubleValue = PriceAmountModel.validateDouble(value); + if (doubleValue == null) { + return appLocalizations.prices_amount_price_incorrect; + } + return null; + } + + // price without discount: only visible if discounted. + if (value == null || value.isEmpty) { + return null; + } + final double? doubleValue = PriceAmountModel.validateDouble(value); + if (doubleValue == null) { + return appLocalizations.prices_amount_price_incorrect; + } + return null; + }, + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_amount_model.dart b/packages/smooth_app/lib/pages/prices/price_amount_model.dart new file mode 100644 index 00000000000..d635342fb33 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_amount_model.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; + +/// Model for the price of a single product. +class PriceAmountModel { + PriceAmountModel({ + required this.product, + }); + + PriceMetaProduct product; + + String _paidPrice = ''; + String _priceWithoutDiscount = ''; + + set paidPrice(final String value) => _paidPrice = value; + + set priceWithoutDiscount(final String value) => _priceWithoutDiscount = value; + + late double _checkedPaidPrice; + double? _checkedPriceWithoutDiscount; + + double get checkedPaidPrice => _checkedPaidPrice; + + double? get checkedPriceWithoutDiscount => _checkedPriceWithoutDiscount; + + bool promo = false; + + static double? validateDouble(final String value) => + double.tryParse(value) ?? + double.tryParse( + value.replaceAll(',', '.'), + ); + + String? checkParameters(final BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + if (product.barcode.isEmpty) { + return appLocalizations.prices_amount_no_product; + } + _checkedPaidPrice = validateDouble(_paidPrice)!; + _checkedPriceWithoutDiscount = null; + if (promo) { + if (_priceWithoutDiscount.isNotEmpty) { + _checkedPriceWithoutDiscount = validateDouble(_priceWithoutDiscount); + if (_checkedPriceWithoutDiscount == null) { + return appLocalizations.prices_amount_price_incorrect; + } + } + } + return null; + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_button.dart b/packages/smooth_app/lib/pages/prices/price_button.dart new file mode 100644 index 00000000000..8b68413bf5e --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_button.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +/// Simple price button: displaying data with optional action. +class PriceButton extends StatelessWidget { + const PriceButton({ + this.title, + this.iconData, + this.buttonStyle, + this.tooltip, + required this.onPressed, + }); + + final String? title; + final IconData? iconData; + final ButtonStyle? buttonStyle; + final VoidCallback? onPressed; + final String? tooltip; + + @override + Widget build(BuildContext context) { + final Widget widget; + + if (iconData == null) { + widget = ElevatedButton( + onPressed: onPressed, + style: buttonStyle, + child: Text(title!), + ); + } else if (title == null) { + widget = ElevatedButton( + onPressed: onPressed, + style: buttonStyle, + child: Icon(iconData), + ); + } else { + widget = ElevatedButton.icon( + onPressed: onPressed, + icon: Icon(iconData), + label: Text(title!), + style: buttonStyle, + ); + } + + if (tooltip?.isNotEmpty == true) { + return Semantics( + value: tooltip, + button: true, + excludeSemantics: true, + child: Tooltip( + message: tooltip, + child: widget, + ), + ); + } + return widget; + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_count_widget.dart b/packages/smooth_app/lib/pages/prices/price_count_widget.dart new file mode 100644 index 00000000000..0b1fa92a402 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_count_widget.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/database/dao_product.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/pages/prices/get_prices_model.dart'; +import 'package:smooth_app/pages/prices/price_button.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; +import 'package:smooth_app/pages/prices/prices_page.dart'; + +/// Price Count display. +class PriceCountWidget extends StatelessWidget { + const PriceCountWidget( + this.count, { + required this.priceProduct, + required this.enableCountButton, + }); + + final int count; + final PriceProduct priceProduct; + final bool enableCountButton; + + @override + Widget build(BuildContext context) => PriceButton( + onPressed: !enableCountButton + ? null + : () async { + final LocalDatabase localDatabase = + context.read(); + final Product? newProduct = + await DaoProduct(localDatabase).get(priceProduct.code); + if (!context.mounted) { + return; + } + return Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => PricesPage( + GetPricesModel.product( + product: newProduct != null + ? PriceMetaProduct.product(newProduct) + : PriceMetaProduct.priceProduct(priceProduct), + context: context, + ), + ), + ), + ); + }, + iconData: Icons.label, + title: '$count', + buttonStyle: ElevatedButton.styleFrom( + disabledForegroundColor: + enableCountButton ? null : getForegroundColor(count), + disabledBackgroundColor: + enableCountButton ? null : getBackgroundColor(count), + foregroundColor: + !enableCountButton ? null : getForegroundColor(count), + backgroundColor: + !enableCountButton ? null : getBackgroundColor(count), + ), + ); + + static Color? getForegroundColor(final int count) => switch (count) { + 0 => Colors.red, + 1 => Colors.orange, + _ => Colors.green, + }; + + static Color? getBackgroundColor(final int count) => switch (count) { + 0 => Colors.red[100], + 1 => Colors.orange[100], + _ => Colors.green[100], + }; +} diff --git a/packages/smooth_app/lib/pages/prices/price_currency_card.dart b/packages/smooth_app/lib/pages/prices/price_currency_card.dart new file mode 100644 index 00000000000..fb82c1bf94e --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_currency_card.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/pages/prices/price_currency_selector.dart'; + +/// Card that displays the currency for price adding. +class PriceCurrencyCard extends StatelessWidget { + const PriceCurrencyCard(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothCard( + child: Column( + children: [ + Text(appLocalizations.prices_currency_subtitle), + PriceCurrencySelector(), + ], + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_currency_selector.dart b/packages/smooth_app/lib/pages/prices/price_currency_selector.dart new file mode 100644 index 00000000000..f0941f22db1 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_currency_selector.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; +import 'package:smooth_app/pages/onboarding/currency_selector_helper.dart'; +import 'package:smooth_app/pages/prices/currency_extension.dart'; + +/// Button that displays the currency for price adding. +class PriceCurrencySelector extends StatelessWidget { + PriceCurrencySelector(); + + final CurrencySelectorHelper helper = CurrencySelectorHelper(); + + @override + Widget build(BuildContext context) { + // TODO(monsieurtanuki): use PriceModel for currency? + final UserPreferences userPreferences = context.watch(); + final Currency selected = helper.getSelected( + userPreferences.userCurrencyCode, + ); + return SmoothLargeButtonWithIcon( + onPressed: () async { + final Currency? currency = await helper.openCurrencySelector( + context: context, + selected: selected, + ); + if (currency != null) { + await userPreferences.setUserCurrencyCode(currency.name); + } + }, + text: selected.getFullName(), + icon: helper.currencyIconData, + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_data_widget.dart b/packages/smooth_app/lib/pages/prices/price_data_widget.dart new file mode 100644 index 00000000000..b246c3d3bb5 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_data_widget.dart @@ -0,0 +1,160 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:intl/intl.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/prices/emoji_helper.dart'; +import 'package:smooth_app/pages/prices/get_prices_model.dart'; +import 'package:smooth_app/pages/prices/price_button.dart'; +import 'package:smooth_app/pages/prices/price_proof_page.dart'; +import 'package:smooth_app/pages/prices/price_user_button.dart'; +import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; +import 'package:smooth_app/query/product_query.dart'; + +/// Price Data display (no product data here). +class PriceDataWidget extends StatelessWidget { + const PriceDataWidget( + this.price, { + required this.model, + }); + + final Price price; + final GetPricesModel model; + + @override + Widget build(BuildContext context) { + final String locale = ProductQuery.getLocaleString(); + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final DateFormat dateFormat = DateFormat.yMd(locale); + final DateFormat timeFormat = DateFormat.Hms(locale); + final NumberFormat currencyFormat = NumberFormat.simpleCurrency( + locale: locale, + name: price.currency.name, + ); + final String? locationTitle = _getLocationTitle(price.location); + + String? getPricePerKg() { + if (price.product == null) { + return null; + } + if (price.product!.quantity == null) { + return null; + } + if ((price.product!.quantityUnit ?? 'g') != 'g') { + return null; + } + return '${currencyFormat.format(price.price / (price.product!.quantity! / 1000))} / kg'; + } + + String? getNotDiscountedPrice() { + if (price.product == null) { + return null; + } + if (price.priceIsDiscounted != true) { + return null; + } + if (price.priceWithoutDiscount == null) { + return null; + } + return '${appLocalizations.prices_amount_price_not_discounted} ${currencyFormat.format(price.priceWithoutDiscount)}'; + } + + final String? pricePerKg = getPricePerKg(); + final String? notDiscountedPrice = getNotDiscountedPrice(); + + final String priceLabel = '${currencyFormat.format(price.price)}' + ' ${pricePerKg == null ? '' : ' ($pricePerKg)'}'; + return Semantics( + container: true, + explicitChildNodes: false, + label: appLocalizations.prices_entry_accessibility_label( + priceLabel, + locationTitle ?? '-', + dateFormat.format(price.date), + price.owner, + ), + child: Wrap( + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: MEDIUM_SPACE, + children: [ + ExcludeSemantics(child: Text(priceLabel)), + ExcludeSemantics(child: Text(dateFormat.format(price.date))), + if (notDiscountedPrice != null) Text('($notDiscountedPrice)'), + if (locationTitle != null) + // TODO(monsieurtanuki): open a still-to-be-done "price x location" page + ExcludeSemantics( + child: PriceButton( + title: locationTitle, + iconData: Icons.location_on_outlined, + onPressed: () {}, + ), + ), + if (model.displayOwner) PriceUserButton(price.owner), + ExcludeSemantics( + child: Tooltip( + message: '${dateFormat.format(price.created)}' + ' ' + '${timeFormat.format(price.created)}', + child: PriceButton( + // TODO(monsieurtanuki): misleading "active" button + onPressed: () {}, + iconData: Icons.history, + title: ProductQueryPageHelper.getDurationStringFromTimestamp( + price.created.millisecondsSinceEpoch, + context, + compact: true, + ), + ), + ), + ), + if (price.proof?.filePath != null) + PriceButton( + iconData: Icons.image, + tooltip: appLocalizations.prices_open_proof, + onPressed: () async => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => PriceProofPage( + price.proof!, + ), + ), + ), // PriceProofPage + ), + ], + ), + ); + } + + static String? _getLocationTitle(final Location? location) { + if (location == null) { + return null; + } + final StringBuffer result = StringBuffer(); + final String? countryEmoji = EmojiHelper().getCountryEmoji( + _getCountry(location), + ); + if (location.name != null) { + result.write(location.name); + } + if (location.city != null) { + if (result.isNotEmpty) { + result.write(', '); + } + result.write(location.city); + } + if (countryEmoji != null) { + if (result.isNotEmpty) { + result.write(' '); + } + result.write(countryEmoji); + } + if (result.isEmpty) { + return null; + } + return result.toString(); + } + + static OpenFoodFactsCountry? _getCountry(final Location location) => + OpenFoodFactsCountry.fromOffTag(location.countryCode); +} diff --git a/packages/smooth_app/lib/pages/prices/price_date_card.dart b/packages/smooth_app/lib/pages/prices/price_date_card.dart new file mode 100644 index 00000000000..7a48e3864c3 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_date_card.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/pages/prices/price_model.dart'; +import 'package:smooth_app/query/product_query.dart'; + +/// Card that displays the date for price adding. +class PriceDateCard extends StatelessWidget { + const PriceDateCard(); + + @override + Widget build(BuildContext context) { + final PriceModel model = context.watch(); + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final String locale = ProductQuery.getLocaleString(); + final DateFormat dateFormat = DateFormat.yMd(locale); + return SmoothCard( + child: Column( + children: [ + Text(appLocalizations.prices_date_subtitle), + SmoothLargeButtonWithIcon( + text: dateFormat.format(model.date), + icon: Icons.calendar_month, + onPressed: () async { + final DateTime? newDate = await showDatePicker( + context: context, + locale: Locale(ProductQuery.getLanguage().offTag), + firstDate: model.firstDate, + lastDate: model.today, + builder: (final BuildContext context, final Widget? child) { + // for some reason we don't have a fine display without that theme. + // cf. https://stackoverflow.com/questions/50321182/how-to-customize-a-date-picker + final ThemeData themeData = + Theme.of(context).brightness == Brightness.light + ? ThemeData.light() + : ThemeData.dark(); + return Theme( + data: themeData.copyWith(), + child: child!, + ); + }, + ); + if (newDate == null) { + return; + } + model.date = newDate; + }, + ), + ], + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_location_card.dart b/packages/smooth_app/lib/pages/prices/price_location_card.dart new file mode 100644 index 00000000000..bd3d9f14998 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_location_card.dart @@ -0,0 +1,76 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/database/dao_osm_location.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:smooth_app/pages/locations/search_location_helper.dart'; +import 'package:smooth_app/pages/locations/search_location_preloaded_item.dart'; +import 'package:smooth_app/pages/prices/price_model.dart'; +import 'package:smooth_app/pages/scan/search_page.dart'; + +/// Card that displays the location for price adding. +class PriceLocationCard extends StatelessWidget { + const PriceLocationCard(); + + static const IconData _iconTodo = CupertinoIcons.exclamationmark; + static const IconData _iconDone = Icons.place; + + @override + Widget build(BuildContext context) { + final PriceModel model = context.watch(); + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final OsmLocation? location = model.location; + return SmoothCard( + child: Column( + children: [ + Text(appLocalizations.prices_location_subtitle), + SmoothLargeButtonWithIcon( + text: location == null + ? appLocalizations.prices_location_find + : location.getTitle() ?? + location.getSubtitle() ?? + location.getLatLng().toString(), + icon: location == null ? _iconTodo : _iconDone, + onPressed: () async { + final LocalDatabase localDatabase = context.read(); + final List preloadedList = + []; + for (final OsmLocation osmLocation in model.locations) { + preloadedList.add( + SearchLocationPreloadedItem( + osmLocation, + popFirst: false, + ), + ); + } + final OsmLocation? osmLocation = + await Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => SearchPage( + const SearchLocationHelper(), + preloadedList: preloadedList, + autofocus: false, + ), + ), + ); + if (osmLocation == null) { + return; + } + final DaoOsmLocation daoOsmLocation = + DaoOsmLocation(localDatabase); + await daoOsmLocation.put(osmLocation); + final List newOsmLocations = + await daoOsmLocation.getAll(); + model.locations = newOsmLocations; + }, + ), + ], + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_meta_product.dart b/packages/smooth_app/lib/pages/prices/price_meta_product.dart new file mode 100644 index 00000000000..cfda26b292e --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_meta_product.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_product_image.dart'; +import 'package:smooth_app/helpers/product_cards_helper.dart'; + +/// Meta version of a product, coming from OFF or from Prices. +class PriceMetaProduct { + PriceMetaProduct.product(final Product product) + : _product = product, + _priceProduct = null, + _barcode = null; + PriceMetaProduct.priceProduct(final PriceProduct priceProduct) + : _product = null, + _priceProduct = priceProduct, + _barcode = null; + PriceMetaProduct.empty() + : _product = null, + _priceProduct = null, + _barcode = null; + PriceMetaProduct.unknown(final String barcode) + : _product = null, + _priceProduct = null, + _barcode = barcode; + + final Product? _product; + final PriceProduct? _priceProduct; + final String? _barcode; + + // TODO(monsieurtanuki): refine this test + bool get isValid => barcode.length >= 8; + + String get barcode { + if (_product != null) { + return _product.barcode!; + } + if (_priceProduct != null) { + return _priceProduct.code; + } + return _barcode ?? ''; + } + + String getName(final AppLocalizations appLocalizations) { + if (_product != null) { + return getProductNameAndBrands( + _product, + appLocalizations, + ); + } + if (_priceProduct != null) { + return _priceProduct.name ?? _priceProduct.code; + } + if (barcode.isEmpty) { + return appLocalizations.prices_barcode_search_none_yet; + } + return appLocalizations.prices_barcode_search_not_found; + } + + Widget getImageWidget(final double size) { + if (_product != null) { + return SmoothMainProductImage( + product: _product, + width: size, + height: size, + ); + } + if (_priceProduct != null) { + final String? imageURL = _priceProduct.imageURL; + return SmoothImage( + width: size, + height: size, + imageProvider: imageURL == null ? null : NetworkImage(imageURL), + ); + } + return SmoothImage( + width: size, + height: size, + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_model.dart b/packages/smooth_app/lib/pages/prices/price_model.dart new file mode 100644 index 00000000000..ae5978231ac --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_model.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/background/background_task_add_price.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:smooth_app/pages/onboarding/currency_selector_helper.dart'; +import 'package:smooth_app/pages/prices/price_amount_model.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; + +/// Price Model (checks and background task call) for price adding. +class PriceModel with ChangeNotifier { + PriceModel({ + required final ProofType proofType, + required final List locations, + required final PriceMetaProduct product, + }) : _proofType = proofType, + _date = DateTime.now(), + _locations = locations, + priceAmountModels = [ + PriceAmountModel(product: product), + ]; + + final List priceAmountModels; + + CropParameters? _cropParameters; + + CropParameters? get cropParameters => _cropParameters; + + set cropParameters(final CropParameters? value) { + _cropParameters = value; + notifyListeners(); + } + + ProofType _proofType; + + ProofType get proofType => _proofType; + + set proofType(final ProofType proofType) { + _proofType = proofType; + notifyListeners(); + } + + DateTime _date; + + DateTime get date => _date; + + set date(final DateTime date) { + _date = date; + notifyListeners(); + } + + final DateTime today = DateTime.now(); + final DateTime firstDate = DateTime.utc(2020, 1, 1); + + late List _locations; + + List get locations => _locations; + + set locations(final List locations) { + _locations = locations; + notifyListeners(); + } + + OsmLocation? get location => _locations.firstOrNull; + + late Currency _checkedCurrency; + + /// Returns the error message of the parameter check, or null if OK. + String? checkParameters(final BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + if (cropParameters == null) { + return appLocalizations.prices_proof_mandatory; + } + + final UserPreferences userPreferences = context.read(); + _checkedCurrency = + CurrencySelectorHelper().getSelected(userPreferences.userCurrencyCode); + + for (final PriceAmountModel priceAmountModel in priceAmountModels) { + final String? checkParameters = priceAmountModel.checkParameters(context); + if (checkParameters != null) { + return checkParameters; + } + } + + if (location == null) { + return appLocalizations.prices_location_mandatory; + } + + return null; + } + + /// Adds the related background task. + Future addTask(final BuildContext context) async { + final List barcodes = []; + final List pricesAreDiscounted = []; + final List prices = []; + final List pricesWithoutDiscount = []; + for (final PriceAmountModel priceAmountModel in priceAmountModels) { + barcodes.add(priceAmountModel.product.barcode); + pricesAreDiscounted.add(priceAmountModel.promo); + prices.add(priceAmountModel.checkedPaidPrice); + pricesWithoutDiscount.add(priceAmountModel.checkedPriceWithoutDiscount); + } + return BackgroundTaskAddPrice.addTask( + context: context, + // per receipt + cropObject: cropParameters!, + locationOSMId: location!.osmId, + locationOSMType: location!.osmType, + date: date, + proofType: proofType, + currency: _checkedCurrency, + // per item + barcodes: barcodes, + pricesAreDiscounted: pricesAreDiscounted, + prices: prices, + pricesWithoutDiscount: pricesWithoutDiscount, + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_product_list_tile.dart b/packages/smooth_app/lib/pages/prices/price_product_list_tile.dart new file mode 100644 index 00000000000..ef3b38d0a30 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_product_list_tile.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; + +/// Displays a meta product with an action button, as a ListTile. +class PriceProductListTile extends StatelessWidget { + const PriceProductListTile({ + required this.product, + this.trailingIconData, + this.onPressed, + }); + + final PriceMetaProduct product; + final IconData? trailingIconData; + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final Size screenSize = MediaQuery.sizeOf(context); + final double size = screenSize.width * 0.20; + final Widget child = Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: size, + child: product.getImageWidget(size), + ), + const SizedBox(width: SMALL_SPACE), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(product.getName(appLocalizations)), + Text(product.barcode), + ], + ), + ), + if (trailingIconData != null) Icon(trailingIconData), + ], + ); + if (onPressed == null) { + return child; + } + return InkWell( + onTap: onPressed, + child: child, + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_product_search_page.dart b/packages/smooth_app/lib/pages/prices/price_product_search_page.dart new file mode 100644 index 00000000000..fcfc88b81fb --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_product_search_page.dart @@ -0,0 +1,239 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/fetched_product.dart'; +import 'package:smooth_app/database/dao_product.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; +import 'package:smooth_app/generic_lib/loading_dialog.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_text_form_field.dart'; +import 'package:smooth_app/helpers/camera_helper.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; +import 'package:smooth_app/pages/prices/price_product_list_tile.dart'; +import 'package:smooth_app/pages/prices/price_scan_page.dart'; +import 'package:smooth_app/pages/product/common/product_refresher.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Product Search Page, for Prices. +class PriceProductSearchPage extends StatefulWidget { + const PriceProductSearchPage({ + this.product, + }); + + final PriceMetaProduct? product; + + // TODO(monsieurtanuki): as a parameter, add a list of barcodes already there: we're not supposed to select twice the same product + + @override + State createState() => _PriceProductSearchPageState(); +} + +class _PriceProductSearchPageState extends State + with TraceableClientMixin { + final TextEditingController _controller = TextEditingController(); + + late PriceMetaProduct? _product = widget.product; + + // TODO(monsieurtanuki): TextInputAction + focus + static const TextInputType _textInputType = TextInputType.number; + + @override + void initState() { + super.initState(); + _controller.text = _product?.barcode ?? ''; + } + + static const String _barcodeHint = '7300400481588'; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final PriceMetaProduct priceMetaProduct = + _product ?? PriceMetaProduct.unknown(_controller.text); + // TODO(monsieurtanuki): add WillPopScope2 + return SmoothScaffold( + appBar: SmoothAppBar( + centerTitle: false, + leading: const SmoothBackButton(), + title: Text(appLocalizations.prices_barcode_search_title), + ), + floatingActionButton: !CameraHelper.hasACamera + ? null + : FloatingActionButton.extended( + onPressed: () async => _scan(context), + label: Text(appLocalizations.prices_barcode_reader_action), + icon: const Icon(Icons.barcode_reader), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(LARGE_SPACE), + child: Column( + children: [ + // TODO(monsieurtanuki): add a "clear" button + // TODO(monsieurtanuki): add an automatic "validate barcode" feature (cf. https://en.wikipedia.org/wiki/International_Article_Number#Check_digit) + SmoothTextFormField( + type: TextFieldTypes.PLAIN_TEXT, + controller: _controller, + hintText: _barcodeHint, + textInputType: _textInputType, + onChanged: (_) async => _onChanged(context), + onFieldSubmitted: (_) async => _onFieldSubmitted(context), + prefixIcon: const Icon(CupertinoIcons.barcode), + textInputAction: TextInputAction.search, + ), + if (priceMetaProduct.isValid) + Padding( + padding: const EdgeInsets.symmetric(vertical: LARGE_SPACE), + child: PriceProductListTile( + product: priceMetaProduct, + trailingIconData: Icons.check_circle, + onPressed: () => Navigator.of(context).pop(priceMetaProduct), + ), + ), + ], + ), + ), + ); + } + + Future _localSearch( + final String barcode, + final LocalDatabase localDatabase, + ) async => + DaoProduct(localDatabase).get(barcode); + + Future _serverSearch( + final String barcode, + final LocalDatabase localDatabase, + final BuildContext context, + ) async { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final FetchedProduct? fetchAndRefreshed = + await LoadingDialog.run( + future: ProductRefresher().silentFetchAndRefresh( + localDatabase: localDatabase, + barcode: barcode, + ), + context: context, + title: appLocalizations.prices_barcode_search_running(barcode), + ); + if (fetchAndRefreshed == null) { + // the user probably cancelled + return null; + } + if (fetchAndRefreshed.product == null) { + if (context.mounted) { + await LoadingDialog.error( + context: context, + title: fetchAndRefreshed.getErrorTitle(appLocalizations), + ); + } + } + return fetchAndRefreshed.product; + } + + // Probably there's a regexp for that, but at least it's readable code. + String _getCleanBarcode(final String input) { + const int ascii0 = 48; + const int ascii9 = 48 + 10 - 1; + + final StringBuffer buffer = StringBuffer(); + for (int i = 0; i < input.length; i++) { + final int charCode = input.codeUnitAt(i); + if (charCode >= ascii0 && charCode <= ascii9) { + buffer.writeCharCode(charCode); + } + } + return buffer.toString(); + } + + Future _onChanged(final BuildContext context) async { + final String barcode = _controller.text; + final String cleanBarcode = _getCleanBarcode(barcode); + if (barcode != cleanBarcode) { + setState(() => _controller.text = cleanBarcode); + return; + } + + if (_product != null) { + setState(() => _product = null); + } + + final LocalDatabase localDatabase = context.read(); + final Product? product = await _localSearch( + barcode, + localDatabase, + ); + if (product != null) { + setState(() => _product = PriceMetaProduct.product(product)); + return; + } + setState(() {}); + } + + Future _onFieldSubmitted(final BuildContext context) async { + final String barcode = _controller.text; + if (barcode.isEmpty) { + return; + } + + final LocalDatabase localDatabase = context.read(); + final Product? product = await _serverSearch( + barcode, + localDatabase, + context, + ); + if (product != null) { + setState(() => _product = PriceMetaProduct.product(product)); + } + } + + Future _scan(final BuildContext context) async { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final String? barcode = await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => const PriceScanPage(), + ), + ); + if (barcode == null) { + return; + } + _controller.text = barcode; + if (!context.mounted) { + return; + } + await _onChanged(context); + if (_product != null) { + return; + } + if (!context.mounted) { + return; + } + final bool? accepts = await showDialog( + context: context, + builder: (final BuildContext context) => SmoothAlertDialog( + body: Text(appLocalizations.prices_barcode_search_question), + neutralAction: SmoothActionButton( + text: appLocalizations.cancel, + onPressed: () => Navigator.of(context).pop(false), + ), + positiveAction: SmoothActionButton( + text: appLocalizations.yes, + onPressed: () => Navigator.of(context).pop(true), + ), + ), + ); + if (!context.mounted) { + return; + } + if (accepts != true) { + return; + } + await _onFieldSubmitted(context); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_product_widget.dart b/packages/smooth_app/lib/pages/prices/price_product_widget.dart new file mode 100644 index 00000000000..9e142a37a2b --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_product_widget.dart @@ -0,0 +1,124 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; +import 'package:smooth_app/pages/prices/get_prices_model.dart'; +import 'package:smooth_app/pages/prices/price_button.dart'; +import 'package:smooth_app/pages/prices/price_count_widget.dart'; + +/// Price Product display (no price data here). +class PriceProductWidget extends StatelessWidget { + const PriceProductWidget( + this.priceProduct, { + required this.model, + }); + + final PriceProduct priceProduct; + final GetPricesModel model; + + @override + Widget build(BuildContext context) { + final Size screenSize = MediaQuery.sizeOf(context); + final double size = screenSize.width * 0.20; + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final String name = priceProduct.name ?? priceProduct.code; + final bool unknown = priceProduct.name == null; + final String? imageURL = priceProduct.imageURL; + final int priceCount = priceProduct.priceCount; + final List? brands = priceProduct.brands?.split(','); + final String? quantity = priceProduct.quantity == null + ? null + : '${priceProduct.quantity} ${priceProduct.quantityUnit ?? 'g'}'; + return Semantics( + label: _generateSemanticsLabel( + appLocalizations, + name, + brands, + quantity, + priceCount, + ), + container: true, + excludeSemantics: true, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: size, + child: SmoothImage( + width: size, + height: size, + imageProvider: imageURL == null ? null : NetworkImage(imageURL), + ), + ), + const SizedBox(width: SMALL_SPACE), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AutoSizeText( + name, + maxLines: 2, + style: Theme.of(context).textTheme.titleMedium, + ), + Wrap( + spacing: VERY_SMALL_SPACE, + crossAxisAlignment: WrapCrossAlignment.center, + runSpacing: 0, + children: [ + PriceCountWidget( + priceCount, + priceProduct: priceProduct, + enableCountButton: model.enableCountButton, + ), + if (brands != null) + for (final String brand in brands) + PriceButton( + title: brand, + onPressed: () {}, + ), + if (quantity != null) Text(quantity), + if (unknown) + PriceButton( + title: appLocalizations.prices_unknown_product, + iconData: Icons.warning, + onPressed: null, + buttonStyle: ElevatedButton.styleFrom( + disabledForegroundColor: Colors.red, + disabledBackgroundColor: Colors.red[100], + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + String _generateSemanticsLabel( + AppLocalizations appLocalizations, + String productName, + List? brands, + String? quantity, + int priceCount, + ) { + final StringBuffer product = StringBuffer(productName); + if (brands?.isNotEmpty == true) { + product.write(' - ${brands!.join(', ')}'); + } + if (quantity?.isNotEmpty == true) { + product.write(' ($quantity)'); + } + + return appLocalizations.prices_product_accessibility_summary( + priceCount, + product.toString(), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_proof_card.dart b/packages/smooth_app/lib/pages/prices/price_proof_card.dart new file mode 100644 index 00000000000..d972ae2932a --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_proof_card.dart @@ -0,0 +1,91 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; +import 'package:smooth_app/pages/image_crop_page.dart'; +import 'package:smooth_app/pages/prices/price_model.dart'; +import 'package:smooth_app/pages/proof_crop_helper.dart'; + +/// Card that displays the proof for price adding. +class PriceProofCard extends StatelessWidget { + const PriceProofCard(); + + static const IconData _iconTodo = CupertinoIcons.exclamationmark; + static const IconData _iconDone = Icons.receipt; + + @override + Widget build(BuildContext context) { + final PriceModel model = context.watch(); + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothCard( + child: Column( + children: [ + Text(appLocalizations.prices_proof_subtitle), + if (model.cropParameters != null) + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) => + Image( + image: FileImage( + File(model.cropParameters!.smallCroppedFile.path), + ), + width: constraints.maxWidth, + height: constraints.maxWidth, + ), + ), + //Text(model.cropParameters!.smallCroppedFile.path), + SmoothLargeButtonWithIcon( + text: model.cropParameters == null + ? appLocalizations.prices_proof_find + : model.proofType == ProofType.receipt + ? appLocalizations.prices_proof_receipt + : appLocalizations.prices_proof_price_tag, + icon: model.cropParameters == null ? _iconTodo : _iconDone, + onPressed: () async { + final CropParameters? cropParameters = + await confirmAndUploadNewImage( + context, + cropHelper: ProofCropHelper(model: model), + isLoggedInMandatory: true, + ); + if (cropParameters != null) { + model.cropParameters = cropParameters; + } + }, + ), + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) => Row( + children: [ + SizedBox( + width: constraints.maxWidth / 2, + child: RadioListTile( + title: Text(appLocalizations.prices_proof_receipt), + value: ProofType.receipt, + groupValue: model.proofType, + onChanged: (final ProofType? proofType) => + model.proofType = proofType!, + ), + ), + SizedBox( + width: constraints.maxWidth / 2, + child: RadioListTile( + title: Text(appLocalizations.prices_proof_price_tag), + value: ProofType.priceTag, + groupValue: model.proofType, + onChanged: (final ProofType? proofType) => + model.proofType = proofType!, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_proof_page.dart b/packages/smooth_app/lib/pages/prices/price_proof_page.dart new file mode 100644 index 00000000000..d0ddf0eabdf --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_proof_page.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:intl/intl.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/helpers/launch_url_helper.dart'; +import 'package:smooth_app/query/product_query.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Full page display of a proof. +class PriceProofPage extends StatelessWidget { + const PriceProofPage( + this.proof, + ); + + final Proof proof; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final DateFormat dateFormat = + DateFormat.yMd(ProductQuery.getLocaleString()).add_Hms(); + return SmoothScaffold( + appBar: SmoothAppBar( + title: Text(appLocalizations.user_search_proof_title), + subTitle: Text(dateFormat.format(proof.created)), + actions: [ + IconButton( + tooltip: appLocalizations.prices_app_button, + icon: const Icon(Icons.open_in_new), + onPressed: () async => LaunchUrlHelper.launchURL(_getUrl()), + ), + ], + ), + body: Image( + image: NetworkImage(_getUrl()), + fit: BoxFit.cover, + ), + ); + } + + String _getUrl() => proof + .getFileUrl(uriProductHelper: ProductQuery.uriProductHelper) + .toString(); +} diff --git a/packages/smooth_app/lib/pages/prices/price_scan_page.dart b/packages/smooth_app/lib/pages/prices/price_scan_page.dart new file mode 100644 index 00000000000..1b12cabd097 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_scan_page.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:smooth_app/helpers/analytics_helper.dart'; +import 'package:smooth_app/helpers/camera_helper.dart'; +import 'package:smooth_app/helpers/global_vars.dart'; +import 'package:smooth_app/helpers/haptic_feedback_helper.dart'; +import 'package:smooth_app/pages/scan/camera_scan_page.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Page showing the camera feed and decoding the first barcode, for Prices. +class PriceScanPage extends StatefulWidget { + const PriceScanPage(); + + @override + State createState() => _PriceScanPageState(); +} + +class _PriceScanPageState extends State + with TraceableClientMixin { + // Mutual exclusion needed: we typically receive several times the same + // barcode and the `pop` would be called several times and cause an error like + // `Failed assertion: line 5277 pos 12: '!_debugLocked': is not true.` + bool _mutex = false; + + @override + String get actionName => + 'Opened ${GlobalVars.barcodeScanner.getType()}_page for price'; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothScaffold( + body: GlobalVars.barcodeScanner.getScanner( + onScan: (final String barcode) async { + if (_mutex) { + return false; + } + _mutex = true; + Navigator.of(context).pop(barcode); + return true; + }, + hapticFeedback: () => SmoothHapticFeedback.click(), + onCameraFlashError: CameraScannerPage.onCameraFlashError, + trackCustomEvent: AnalyticsHelper.trackCustomEvent, + hasMoreThanOneCamera: CameraHelper.hasMoreThanOneCamera, + toggleCameraModeTooltip: appLocalizations.camera_toggle_camera, + toggleFlashModeTooltip: appLocalizations.camera_toggle_flash, + contentPadding: null, + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/price_user_button.dart b/packages/smooth_app/lib/pages/prices/price_user_button.dart new file mode 100644 index 00000000000..d7bcd09da52 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/price_user_button.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/pages/prices/get_prices_model.dart'; +import 'package:smooth_app/pages/prices/price_button.dart'; +import 'package:smooth_app/pages/prices/prices_page.dart'; +import 'package:smooth_app/query/product_query.dart'; + +/// Widget that displays a user, for Prices. +class PriceUserButton extends StatelessWidget { + const PriceUserButton(this.user); + + final String user; + + static String showUserTitle({ + required final String user, + required final BuildContext context, + }) => + user == ProductQuery.getWriteUser().userId + ? AppLocalizations.of(context).user_search_prices_title + : AppLocalizations.of(context).user_any_search_prices_title; + + static Future showUserPrices({ + required final String user, + required final BuildContext context, + }) async => + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => PricesPage( + GetPricesModel( + parameters: GetPricesParameters() + ..owner = user + ..orderBy = >[ + const OrderBy( + field: GetPricesOrderField.created, + ascending: false, + ), + ] + ..pageSize = GetPricesModel.pageSize + ..pageNumber = 1, + displayOwner: false, + displayProduct: true, + uri: OpenPricesAPIClient.getUri( + path: 'app/users/$user', + uriHelper: ProductQuery.uriProductHelper, + ), + title: showUserTitle(user: user, context: context), + subtitle: user, + ), + ), + ), + ); + + @override + Widget build(BuildContext context) => PriceButton( + tooltip: AppLocalizations.of(context).prices_open_user_proofs(user), + title: user, + iconData: Icons.account_box, + onPressed: () async => showUserPrices( + user: user, + context: context, + ), + ); +} diff --git a/packages/smooth_app/lib/pages/prices/prices_card.dart b/packages/smooth_app/lib/pages/prices/prices_card.dart new file mode 100644 index 00000000000..1337706f7c5 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/prices_card.dart @@ -0,0 +1,108 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/pages/prices/get_prices_model.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; +import 'package:smooth_app/pages/prices/prices_page.dart'; +import 'package:smooth_app/pages/prices/product_price_add_page.dart'; +import 'package:smooth_app/resources/app_icons.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; + +/// Card that displays buttons related to prices. +class PricesCard extends StatelessWidget { + const PricesCard(this.product); + + final Product product; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final SmoothColorsThemeExtension? themeExtension = + Theme.of(context).extension(); + + return buildProductSmoothCard( + body: Container( + width: double.infinity, + padding: const EdgeInsetsDirectional.all(LARGE_SPACE), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of(context).prices_generic_title, + style: Theme.of(context).textTheme.displaySmall, + ), + const SizedBox(width: SMALL_SPACE), + Container( + decoration: BoxDecoration( + color: themeExtension!.secondaryNormal, + borderRadius: CIRCULAR_BORDER_RADIUS, + ), + margin: const EdgeInsets.only(top: 0.5), + padding: const EdgeInsets.symmetric( + horizontal: MEDIUM_SPACE, + vertical: VERY_SMALL_SPACE, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + appLocalizations.preview_badge, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: SMALL_SPACE), + const Lab( + color: Colors.white, + size: 13.0, + ), + ], + ), + ), + ], + ), + const SizedBox(height: SMALL_SPACE), + Padding( + padding: const EdgeInsets.all(SMALL_SPACE), + child: SmoothLargeButtonWithIcon( + text: appLocalizations.prices_view_prices, + icon: CupertinoIcons.tag_fill, + onPressed: () async => Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => PricesPage( + GetPricesModel.product( + product: PriceMetaProduct.product(product), + context: context, + ), + ), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(SMALL_SPACE), + child: SmoothLargeButtonWithIcon( + text: appLocalizations.prices_add_a_price, + icon: Icons.add, + onPressed: () async => ProductPriceAddPage.showProductPage( + context: context, + product: PriceMetaProduct.product(product), + proofType: ProofType.priceTag, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/prices_page.dart b/packages/smooth_app/lib/pages/prices/prices_page.dart new file mode 100644 index 00000000000..ada617b91d9 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/prices_page.dart @@ -0,0 +1,155 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; +import 'package:smooth_app/helpers/launch_url_helper.dart'; +import 'package:smooth_app/helpers/provider_helper.dart'; +import 'package:smooth_app/pages/prices/get_prices_model.dart'; +import 'package:smooth_app/pages/prices/product_prices_list.dart'; +import 'package:smooth_app/resources/app_icons.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Page that displays the latest prices according to a model. +class PricesPage extends StatelessWidget { + const PricesPage(this.model); + + final GetPricesModel model; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothScaffold( + appBar: SmoothAppBar( + centerTitle: false, + leading: const SmoothBackButton(), + title: Text( + model.title, + maxLines: model.subtitle == null ? 2 : 1, + ), + subTitle: model.subtitle == null ? null : Text(model.subtitle!), + actions: [ + Semantics( + link: true, + label: appLocalizations.prices_app_button, + excludeSemantics: true, + child: IconButton( + tooltip: appLocalizations.prices_app_button, + icon: const ExcludeSemantics(child: Icon(Icons.open_in_new)), + onPressed: () async => LaunchUrlHelper.launchURL( + model.uri.toString(), + ), + ), + ), + ], + ), + body: ProductPricesList(model), + floatingActionButton: model.addButton == null + ? null + : FloatingActionButton.extended( + onPressed: model.addButton, + label: Text(appLocalizations.prices_add_a_price), + icon: const Icon(Icons.add), + ), + bottomNavigationBar: ConsumerFilter( + buildWhen: + (UserPreferences? previousValue, UserPreferences currentValue) => + previousValue?.shouldShowPricesFeedbackForm != + currentValue.shouldShowPricesFeedbackForm, + builder: ( + final BuildContext context, + final UserPreferences userPreferences, + _, + ) { + if (!userPreferences.shouldShowPricesFeedbackForm) { + return EMPTY_WIDGET; + } + + return const _PricesFeedbackForm(); + }, + ), + ); + } +} + +class _PricesFeedbackForm extends StatelessWidget { + const _PricesFeedbackForm(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + final SmoothColorsThemeExtension? themeExtension = + Theme.of(context).extension(); + + final double bottomPadding = MediaQuery.viewPaddingOf(context).bottom; + + return Ink( + width: double.infinity, + height: kBottomNavigationBarHeight + bottomPadding, + color: themeExtension!.primaryBlack, + padding: EdgeInsetsDirectional.only(bottom: bottomPadding), + child: IconTheme( + data: const IconThemeData(color: Colors.white), + child: InkWell( + onTap: () async { + LaunchUrlHelper.launchURL( + 'https://forms.gle/Vmh9SR3HhPpjMnVF7', + ); + context.read().markPricesFeedbackFormAsCompleted(); + }, + child: Padding( + padding: const EdgeInsetsDirectional.symmetric( + horizontal: MEDIUM_SPACE, + vertical: SMALL_SPACE, + ), + child: Row( + children: [ + ExcludeSemantics( + child: Container( + decoration: BoxDecoration( + color: themeExtension.secondaryNormal, + shape: BoxShape.circle, + ), + child: const AspectRatio( + aspectRatio: 1.0, + child: Lab( + color: Colors.white, + size: 13.0, + ), + ), + ), + ), + const SizedBox(width: SMALL_SPACE), + Expanded( + child: AutoSizeText( + appLocalizations.prices_feedback_form, + maxLines: 2, + style: const TextStyle( + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + const SizedBox(width: SMALL_SPACE), + InkWell( + customBorder: const CircleBorder(), + onTap: () => context + .read() + .markPricesFeedbackFormAsCompleted(), + child: const AspectRatio( + aspectRatio: 1.0, + child: CloseButtonIcon(), + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/prices_proofs_page.dart b/packages/smooth_app/lib/pages/prices/prices_proofs_page.dart new file mode 100644 index 00000000000..fac691981f4 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/prices_proofs_page.dart @@ -0,0 +1,235 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:intl/intl.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/helpers/launch_url_helper.dart'; +import 'package:smooth_app/pages/prices/price_proof_page.dart'; +import 'package:smooth_app/query/product_query.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Page that displays the latest proofs of the current user. +class PricesProofsPage extends StatefulWidget { + const PricesProofsPage(); + + @override + State createState() => _PricesProofsPageState(); +} + +class _PricesProofsPageState extends State + with TraceableClientMixin { + late final Future> _results = _download(); + + static const int _columns = 3; + static const int _rows = 5; + static const int _pageSize = _columns * _rows; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothScaffold( + appBar: SmoothAppBar( + centerTitle: false, + leading: const SmoothBackButton(), + title: Text( + appLocalizations.user_search_proofs_title, + ), + actions: [ + IconButton( + tooltip: appLocalizations.prices_app_button, + icon: const Icon(Icons.open_in_new), + onPressed: () async => LaunchUrlHelper.launchURL( + OpenPricesAPIClient.getUri( + path: 'app/dashboard/proofs', + uriHelper: ProductQuery.uriProductHelper, + ).toString(), + ), + ), + ], + ), + body: FutureBuilder>( + future: _results, + builder: ( + final BuildContext context, + final AsyncSnapshot> snapshot, + ) { + if (snapshot.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + if (snapshot.hasError) { + return Text(snapshot.error!.toString()); + } + // highly improbable + if (!snapshot.hasData) { + return const Text('no data'); + } + if (snapshot.data!.isError) { + return Text(snapshot.data!.error!); + } + final GetProofsResult result = snapshot.data!.value; + // highly improbable + if (result.items == null) { + return const Text('empty list'); + } + final double squareSize = MediaQuery.sizeOf(context).width / _columns; + + final AppLocalizations appLocalizations = + AppLocalizations.of(context); + final String title = result.numberOfPages == 1 + ? appLocalizations.prices_proofs_list_length_one_page( + result.items!.length, + ) + : appLocalizations.prices_proofs_list_length_many_pages( + _pageSize, + result.total!, + ); + return Column( + children: [ + SmoothCard( + child: ListTile( + title: Text(title), + ), + ), + if (result.items!.isNotEmpty) + Expanded( + child: CustomScrollView( + slivers: [ + SliverGrid( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: _columns, + ), + delegate: SliverChildBuilderDelegate( + ( + final BuildContext context, + final int index, + ) { + final Proof proof = result.items![index]; + if (proof.filePath == null) { + // highly improbable + return SizedBox( + width: squareSize, + height: squareSize, + ); + } + return InkWell( + onTap: () async => Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => + PriceProofPage( + proof, + ), + ), + ), // PriceProofPage + child: _PriceProofImage(proof, + squareSize: squareSize), + ); + }, + addAutomaticKeepAlives: false, + childCount: result.items!.length, + ), + ), + ], + ), + ), + ], + ); + }, + ), + ); + } + + static Future> _download() async { + final User user = ProductQuery.getWriteUser(); + final MaybeError token = + await OpenPricesAPIClient.getAuthenticationToken( + username: user.userId, + password: user.password, + uriHelper: ProductQuery.uriProductHelper, + ); + final String bearerToken = token.value; + + final MaybeError result = + await OpenPricesAPIClient.getProofs( + GetProofsParameters() + ..orderBy = >[ + const OrderBy( + field: GetProofsOrderField.created, + ascending: false, + ), + ] + ..pageSize = _pageSize + ..pageNumber = 1, + uriHelper: ProductQuery.uriProductHelper, + bearerToken: bearerToken, + ); + + await OpenPricesAPIClient.deleteUserSession( + uriHelper: ProductQuery.uriProductHelper, + bearerToken: bearerToken, + ); + + return result; + } +} + +// TODO(monsieurtanuki): reuse whatever will be coded in https://github.com/openfoodfacts/smooth-app/pull/5366 +class _PriceProofImage extends StatelessWidget { + const _PriceProofImage( + this.proof, { + required this.squareSize, + }); + + final Proof proof; + final double squareSize; + + @override + Widget build(BuildContext context) { + final DateFormat dateFormat = + DateFormat.yMd(ProductQuery.getLocaleString()); + final String date = dateFormat.format(proof.created); + return Stack( + children: [ + SmoothImage( + width: squareSize, + height: squareSize, + imageProvider: NetworkImage( + proof + .getFileUrl( + uriProductHelper: ProductQuery.uriProductHelper, + ) + .toString(), + ), + rounded: false, + ), + SizedBox( + width: squareSize, + height: squareSize, + child: Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.all(SMALL_SPACE), + child: Container( + height: VERY_LARGE_SPACE, + color: Colors.white.withAlpha(128), + child: Center( + child: AutoSizeText( + date, + maxLines: 1, + ), + ), + ), + ), + ), + ), + ], + ); + } +} diff --git a/packages/smooth_app/lib/pages/prices/prices_users_page.dart b/packages/smooth_app/lib/pages/prices/prices_users_page.dart new file mode 100644 index 00000000000..4ec9283aaec --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/prices_users_page.dart @@ -0,0 +1,145 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/helpers/launch_url_helper.dart'; +import 'package:smooth_app/pages/prices/price_button.dart'; +import 'package:smooth_app/pages/prices/price_count_widget.dart'; +import 'package:smooth_app/pages/prices/price_user_button.dart'; +import 'package:smooth_app/query/product_query.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Page that displays the top prices users. +class PricesUsersPage extends StatefulWidget { + const PricesUsersPage(); + + @override + State createState() => _PricesUsersPageState(); +} + +class _PricesUsersPageState extends State + with TraceableClientMixin { + late final Future> _users = _showTopUsers(); + + // In this specific page, let's never try to go beyond the top 10. + // cf. https://github.com/openfoodfacts/smooth-app/pull/5383#issuecomment-2171117141 + static const int _pageSize = 10; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothScaffold( + appBar: SmoothAppBar( + centerTitle: false, + leading: const SmoothBackButton(), + title: Text( + appLocalizations.all_search_prices_top_user_title, + ), + actions: [ + IconButton( + tooltip: appLocalizations.prices_app_button, + icon: const Icon(Icons.open_in_new), + onPressed: () async => LaunchUrlHelper.launchURL( + OpenPricesAPIClient.getUri( + path: 'app/users', + uriHelper: ProductQuery.uriProductHelper, + ).toString(), + ), + ), + ], + ), + body: FutureBuilder>( + future: _users, + builder: ( + final BuildContext context, + final AsyncSnapshot> snapshot, + ) { + if (snapshot.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + if (snapshot.hasError) { + return Text(snapshot.error!.toString()); + } + // highly improbable + if (!snapshot.hasData) { + return const Text('no data'); + } + if (snapshot.data!.isError) { + return Text(snapshot.data!.error!); + } + final GetUsersResult result = snapshot.data!.value; + // highly improbable + if (result.items == null) { + return const Text('empty list'); + } + final List children = []; + + for (final PriceUser item in result.items!) { + children.add( + SmoothCard( + child: Wrap( + spacing: VERY_SMALL_SPACE, + children: [ + PriceUserButton(item.userId), + PriceButton( + onPressed: () async => PriceUserButton.showUserPrices( + user: item.userId, + context: context, + ), + iconData: Icons.label, + title: '${item.priceCount}', + buttonStyle: ElevatedButton.styleFrom( + foregroundColor: PriceCountWidget.getForegroundColor( + item.priceCount, + ), + backgroundColor: PriceCountWidget.getBackgroundColor( + item.priceCount, + ), + ), + ), + ], + ), + ), + ); + } + final AppLocalizations appLocalizations = + AppLocalizations.of(context); + final String title = + appLocalizations.prices_users_list_length_many_pages( + _pageSize, + result.total!, + ); + children.insert( + 0, + SmoothCard(child: ListTile(title: Text(title))), + ); + // so that the last content gets not hidden by the FAB + children.add( + const SizedBox(height: 2 * MINIMUM_TOUCH_SIZE), + ); + return ListView( + children: children, + ); + }, + ), + ); + } + + static Future> _showTopUsers() async => + OpenPricesAPIClient.getUsers( + GetUsersParameters() + ..orderBy = >[ + const OrderBy( + field: GetUsersOrderField.priceCount, + ascending: false, + ), + ] + ..pageSize = _pageSize + ..pageNumber = 1, + uriHelper: ProductQuery.uriProductHelper, + ); +} diff --git a/packages/smooth_app/lib/pages/prices/product_price_add_page.dart b/packages/smooth_app/lib/pages/prices/product_price_add_page.dart new file mode 100644 index 00000000000..733098d9703 --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/product_price_add_page.dart @@ -0,0 +1,253 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/database/dao_osm_location.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/pages/locations/osm_location.dart'; +import 'package:smooth_app/pages/prices/price_amount_card.dart'; +import 'package:smooth_app/pages/prices/price_amount_model.dart'; +import 'package:smooth_app/pages/prices/price_currency_card.dart'; +import 'package:smooth_app/pages/prices/price_date_card.dart'; +import 'package:smooth_app/pages/prices/price_location_card.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; +import 'package:smooth_app/pages/prices/price_model.dart'; +import 'package:smooth_app/pages/prices/price_product_search_page.dart'; +import 'package:smooth_app/pages/prices/price_proof_card.dart'; +import 'package:smooth_app/pages/product/common/product_refresher.dart'; +import 'package:smooth_app/widgets/smooth_app_bar.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +/// Single page that displays all the elements of price adding. +class ProductPriceAddPage extends StatefulWidget { + const ProductPriceAddPage({ + required this.product, + required this.latestOsmLocations, + required this.proofType, + }); + + final PriceMetaProduct product; + final List latestOsmLocations; + final ProofType proofType; + + static Future showProductPage({ + required final BuildContext context, + required final PriceMetaProduct product, + required final ProofType proofType, + }) async { + if (!await ProductRefresher().checkIfLoggedIn( + context, + isLoggedInMandatory: true, + )) { + return; + } + if (!context.mounted) { + return; + } + final LocalDatabase localDatabase = context.read(); + final List osmLocations = + await DaoOsmLocation(localDatabase).getAll(); + if (!context.mounted) { + return; + } + + await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => ProductPriceAddPage( + product: product, + latestOsmLocations: osmLocations, + proofType: proofType, + ), + ), + ); + } + + @override + State createState() => _ProductPriceAddPageState(); +} + +class _ProductPriceAddPageState extends State + with TraceableClientMixin { + late final PriceModel _model = PriceModel( + proofType: widget.proofType, + locations: widget.latestOsmLocations, + product: widget.product, + ); + + final GlobalKey _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + // TODO(monsieurtanuki): add WillPopScope2 + return ChangeNotifierProvider( + create: (_) => _model, + child: Form( + key: _formKey, + child: SmoothScaffold( + appBar: SmoothAppBar( + centerTitle: false, + leading: const SmoothBackButton(), + title: Text( + appLocalizations.prices_add_n_prices( + _model.priceAmountModels.length, + ), + ), + actions: [ + IconButton( + icon: const Icon(Icons.info), + onPressed: () async => _doesAcceptWarning(justInfo: true), + ), + ], + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(LARGE_SPACE), + child: Column( + children: [ + const PriceProofCard(), + const SizedBox(height: LARGE_SPACE), + const PriceDateCard(), + const SizedBox(height: LARGE_SPACE), + const PriceLocationCard(), + const SizedBox(height: LARGE_SPACE), + const PriceCurrencyCard(), + const SizedBox(height: LARGE_SPACE), + for (int i = 0; i < _model.priceAmountModels.length; i++) + PriceAmountCard( + priceModel: _model, + index: i, + refresh: () => setState(() {}), + ), + // TODO(monsieurtanuki): check if there's an empty barcode before displaying this card + SmoothCard( + child: SmoothLargeButtonWithIcon( + text: appLocalizations.prices_add_an_item, + icon: Icons.add, + onPressed: () async { + final PriceMetaProduct? product = + await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => + const PriceProductSearchPage(), + ), + ); + if (product == null) { + return; + } + setState( + () => _model.priceAmountModels.add( + PriceAmountModel( + product: product, + ), + ), + ); + }, + ), + ), + // so that the last items don't get hidden by the FAB + const SizedBox(height: MINIMUM_TOUCH_SIZE * 2), + ], + ), + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () async { + if (!await _check(context)) { + return; + } + if (!context.mounted) { + return; + } + + final UserPreferences userPreferences = + context.read(); + const String flagTag = UserPreferences.TAG_PRICE_PRIVACY_WARNING; + final bool? already = userPreferences.getFlag(flagTag); + if (already != true) { + final bool? accepts = await _doesAcceptWarning(justInfo: false); + if (accepts != true) { + return; + } + await userPreferences.setFlag(flagTag, true); + } + if (!context.mounted) { + return; + } + + await _model.addTask(context); + if (!context.mounted) { + return; + } + Navigator.of(context).pop(); + }, + icon: const Icon(Icons.send), + label: Text( + appLocalizations.prices_send_n_prices( + _model.priceAmountModels.length, + ), + ), + ), + ), + ), + ); + } + + Future _doesAcceptWarning({required final bool justInfo}) async { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return showDialog( + context: context, + builder: (final BuildContext context) => SmoothAlertDialog( + title: appLocalizations.prices_privacy_warning_title, + actionsAxis: Axis.vertical, + body: Text(appLocalizations.prices_privacy_warning_message), + positiveAction: SmoothActionButton( + text: appLocalizations.okay, + onPressed: () => Navigator.of(context).pop(true), + ), + negativeAction: justInfo + ? null + : SmoothActionButton( + text: appLocalizations.cancel, + onPressed: () => Navigator.of(context).pop(), + ), + ), + ); + } + + /// Returns true if the basic checks passed. + Future _check(final BuildContext context) async { + if (!_formKey.currentState!.validate()) { + return false; + } + + String? error; + try { + error = _model.checkParameters(context); + } catch (e) { + error = e.toString(); + } + if (error != null) { + if (!context.mounted) { + return false; + } + await showDialog( + context: context, + builder: (BuildContext context) => SmoothSimpleErrorAlertDialog( + title: AppLocalizations.of(context).prices_add_validation_error, + message: error!, + ), + ); + return false; + } + return true; + } + + @override + String get actionName => 'Opened price_page with ${widget.proofType.offTag}'; +} diff --git a/packages/smooth_app/lib/pages/prices/product_prices_list.dart b/packages/smooth_app/lib/pages/prices/product_prices_list.dart new file mode 100644 index 00000000000..8f30271732d --- /dev/null +++ b/packages/smooth_app/lib/pages/prices/product_prices_list.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:matomo_tracker/matomo_tracker.dart'; +import 'package:openfoodfacts/openfoodfacts.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/prices/get_prices_model.dart'; +import 'package:smooth_app/pages/prices/price_data_widget.dart'; +import 'package:smooth_app/pages/prices/price_product_widget.dart'; +import 'package:smooth_app/query/product_query.dart'; + +/// List of the latest prices for a given model. +class ProductPricesList extends StatefulWidget { + const ProductPricesList( + this.model, + ); + + final GetPricesModel model; + + @override + State createState() => _ProductPricesListState(); +} + +class _ProductPricesListState extends State + with TraceableClientMixin { + late final Future> _prices = + _showProductPrices(widget.model.parameters); + + // TODO(monsieurtanuki): add a refresh gesture + // TODO(monsieurtanuki): add a "download the next 10" items + @override + Widget build(BuildContext context) => + FutureBuilder>( + future: _prices, + builder: ( + final BuildContext context, + final AsyncSnapshot> snapshot, + ) { + if (snapshot.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + if (snapshot.hasError) { + return Text(snapshot.error!.toString()); + } + // highly improbable + if (!snapshot.hasData) { + return const Text('no data'); + } + if (snapshot.data!.isError) { + return Text(snapshot.data!.error!); + } + final GetPricesResult result = snapshot.data!.value; + // highly improbable + if (result.items == null) { + return const Text('empty list'); + } + final List children = []; + + if (!widget.model.displayProduct) { + // in that case we display the product only once, if possible. + for (final Price price in result.items!) { + final PriceProduct? priceProduct = price.product; + if (priceProduct == null) { + continue; + } + children.add( + SmoothCard( + child: PriceProductWidget( + priceProduct, + model: widget.model, + ), + ), + ); + break; + } + } + + for (final Price price in result.items!) { + final PriceProduct? priceProduct = price.product; + children.add( + SmoothCard( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.model.displayProduct && priceProduct != null) + PriceProductWidget( + priceProduct, + model: widget.model, + ), + PriceDataWidget( + price, + model: widget.model, + ), + ], + ), + ), + ); + } + final AppLocalizations appLocalizations = + AppLocalizations.of(context); + final String title = result.numberOfPages == 1 + ? appLocalizations.prices_list_length_one_page( + result.items!.length, + ) + : appLocalizations.prices_list_length_many_pages( + widget.model.parameters.pageSize!, + result.total!, + ); + children.insert( + 0, + SmoothCard(child: ListTile(title: Text(title))), + ); + // so that the last content gets not hidden by the FAB + children.add( + const SizedBox(height: 2 * MINIMUM_TOUCH_SIZE), + ); + return ListView( + children: children, + ); + }, + ); + + static Future> _showProductPrices( + final GetPricesParameters parameters, + ) async => + OpenPricesAPIClient.getPrices( + parameters, + uriHelper: ProductQuery.uriProductHelper, + ); +} diff --git a/packages/smooth_app/lib/pages/product/add_basic_details_page.dart b/packages/smooth_app/lib/pages/product/add_basic_details_page.dart index b6e272b3e48..c59d5fdd03d 100644 --- a/packages/smooth_app/lib/pages/product/add_basic_details_page.dart +++ b/packages/smooth_app/lib/pages/product/add_basic_details_page.dart @@ -1,14 +1,19 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/background/background_task_details.dart'; import 'package:smooth_app/cards/product_cards/product_image_carousel.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_text_form_field.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/helpers/provider_helper.dart'; import 'package:smooth_app/pages/input/smooth_autocomplete_text_field.dart'; import 'package:smooth_app/pages/input/unfocus_field_when_tap_outside.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart'; import 'package:smooth_app/pages/product/common/product_buttons.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/may_exit_page_helper.dart'; @@ -123,31 +128,62 @@ class _AddBasicDetailsPageState extends State { ), ), SizedBox(height: _heightSpace), - if (_multilingualHelper.isMonolingual()) - SmoothTextFormField( - controller: _productNameController, - type: TextFieldTypes.PLAIN_TEXT, - hintText: appLocalizations.product_name, - ) - else - Card( - child: Column( - children: [ - _multilingualHelper.getLanguageSelector( - setState: setState, - product: _product, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: SmoothTextFormField( - controller: _productNameController, - type: TextFieldTypes.PLAIN_TEXT, - hintText: appLocalizations.product_name, - ), + ConsumerFilter( + buildWhen: ( + UserPreferences? previousValue, + UserPreferences currentValue, + ) { + return previousValue?.getFlag(UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr) != + currentValue.getFlag(UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr); + }, + builder: (BuildContext context, UserPreferences prefs, + Widget? child) { + if (_multilingualHelper.isMonolingual()) { + return SmoothTextFormField( + controller: _productNameController, + type: TextFieldTypes.PLAIN_TEXT, + hintText: appLocalizations.product_name, + spellCheckConfiguration: (prefs.getFlag( + UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr) ?? + false) && + (Platform.isAndroid || Platform.isIOS) + ? const SpellCheckConfiguration() + : const SpellCheckConfiguration.disabled(), + ); + } else { + return Card( + child: Column( + children: [ + _multilingualHelper.getLanguageSelector( + setState: setState, + product: _product, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: SmoothTextFormField( + controller: _productNameController, + type: TextFieldTypes.PLAIN_TEXT, + hintText: appLocalizations.product_name, + spellCheckConfiguration: (prefs.getFlag( + UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr) ?? + false) && + (Platform.isAndroid || + Platform.isIOS) + ? const SpellCheckConfiguration() + : const SpellCheckConfiguration + .disabled(), + ), + ), + ], ), - ], - ), - ), + ); + } + }, + ), SizedBox(height: _heightSpace), LayoutBuilder( builder: ( diff --git a/packages/smooth_app/lib/pages/product/add_new_product_helper.dart b/packages/smooth_app/lib/pages/product/add_new_product_helper.dart index 30776480723..feee5aa20ba 100644 --- a/packages/smooth_app/lib/pages/product/add_new_product_helper.dart +++ b/packages/smooth_app/lib/pages/product/add_new_product_helper.dart @@ -1,8 +1,10 @@ +import 'dart:math' as math; + import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/cards/category_cards/svg_cache.dart'; import 'package:smooth_app/data_models/product_image_data.dart'; -import 'package:smooth_app/database/transient_file.dart'; import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/svg_icon_chip.dart'; @@ -10,6 +12,7 @@ import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; import 'package:smooth_app/pages/product/product_field_editor.dart'; import 'package:smooth_app/query/product_query.dart'; +import 'package:smooth_app/resources/app_animations.dart'; /// Tracks (only the first time) when a [check] is true. class AnalyticsProductTracker { @@ -163,24 +166,69 @@ class AddNewProductScoreIcon extends StatelessWidget { final String defaultIconUrl; @override - Widget build(BuildContext context) => SvgIconChip( + Widget build(BuildContext context) { + final String url = iconUrl ?? defaultIconUrl; + final String fileName = Uri.parse(url).pathSegments.last; + final double height = MediaQuery.sizeOf(context).height * .2; + + if (fileName.startsWith('nutriscore')) { + return _AddNewProductNutriScoreIcon( + fileName: fileName, + height: height, + ); + } else { + return SvgIconChip( iconUrl ?? defaultIconUrl, - height: MediaQuery.of(context).size.height * .2, + height: height, ); + } + } +} + +class _AddNewProductNutriScoreIcon extends StatelessWidget { + _AddNewProductNutriScoreIcon({ + required String fileName, + required this.height, + }) : nutriScore = extractValue(fileName); + + final NutriScoreValue nutriScore; + final double height; + + static NutriScoreValue extractValue(String fileName) { + if (fileName.startsWith('nutriscore-a')) { + return NutriScoreValue.a; + } else if (fileName.startsWith('nutriscore-b')) { + return NutriScoreValue.b; + } else if (fileName.startsWith('nutriscore-c')) { + return NutriScoreValue.c; + } else if (fileName.startsWith('nutriscore-d')) { + return NutriScoreValue.d; + } else if (fileName.startsWith('nutriscore-e')) { + return NutriScoreValue.e; + } else { + return NutriScoreValue.unknown; + } + } + + @override + Widget build(BuildContext context) { + return NutriScoreAnimation( + value: nutriScore, + size: Size.fromHeight(math.min(height, 200.0)), + ); + } } /// Helper for the "Add new product" page. class AddNewProductHelper { bool isMainImagePopulated( final ProductImageData productImageData, - final String barcode, + final Product product, ) => - TransientFile.fromProductImageData( - productImageData, - barcode, - ProductQuery.getLanguage(), - ).getImageProvider() != - null; + getProductImageLanguages( + product, + productImageData.imageField, + ).isNotEmpty; bool isOneMainImagePopulated(final Product product) { final List productImagesData = getProductMainImagesData( @@ -189,7 +237,7 @@ class AddNewProductHelper { ProductQuery.getLanguage(), ); for (final ProductImageData productImageData in productImagesData) { - if (isMainImagePopulated(productImageData, product.barcode!)) { + if (isMainImagePopulated(productImageData, product)) { return true; } } diff --git a/packages/smooth_app/lib/pages/product/add_new_product_page.dart b/packages/smooth_app/lib/pages/product/add_new_product_page.dart index c606c543d3e..2b6aa487e87 100644 --- a/packages/smooth_app/lib/pages/product/add_new_product_page.dart +++ b/packages/smooth_app/lib/pages/product/add_new_product_page.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_animation_progress_bar/flutter_animation_progress_bar.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -17,6 +15,7 @@ import 'package:smooth_app/generic_lib/duration_constants.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/image_field_extension.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; import 'package:smooth_app/pages/image_crop_page.dart'; import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; import 'package:smooth_app/pages/product/add_new_product_helper.dart'; @@ -564,7 +563,7 @@ class _AddNewProductPageState extends State ); for (final ProductImageData data in productImagesData) { // Everything else can only be uploaded once - rows.add(_buildMainImageButton(context, data)); + rows.add(_buildMainImageButton(context, upToDateProduct, data)); rows.add( const Padding( padding: EdgeInsets.symmetric(vertical: 10.0), @@ -591,14 +590,15 @@ class _AddNewProductPageState extends State ? AddNewProductButton.doneIconData : AddNewProductButton.cameraIconData, () async { - final File? finalPhoto = await confirmAndUploadNewPicture( + final CropParameters? cropParameters = + await confirmAndUploadNewPicture( context, barcode: barcode, imageField: ImageField.OTHER, language: ProductQuery.getLanguage(), isLoggedInMandatory: widget.isLoggedInMandatory, ); - if (finalPhoto != null) { + if (cropParameters != null) { setState(() => ++_otherCount); } }, @@ -609,9 +609,10 @@ class _AddNewProductPageState extends State /// Button specific to one of the main 4 images. Widget _buildMainImageButton( final BuildContext context, + final Product product, final ProductImageData productImageData, ) { - final bool done = _helper.isMainImagePopulated(productImageData, barcode); + final bool done = _helper.isMainImagePopulated(productImageData, product); return AddNewProductButton( productImageData.imageField .getAddPhotoButtonText(AppLocalizations.of(context)), diff --git a/packages/smooth_app/lib/pages/product/add_other_details_page.dart b/packages/smooth_app/lib/pages/product/add_other_details_page.dart index 93d2d5e68e9..23565fa5be5 100644 --- a/packages/smooth_app/lib/pages/product/add_other_details_page.dart +++ b/packages/smooth_app/lib/pages/product/add_other_details_page.dart @@ -52,7 +52,7 @@ class _AddOtherDetailsPageState extends State { @override Widget build(BuildContext context) { - final Size size = MediaQuery.of(context).size; + final Size size = MediaQuery.sizeOf(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); return WillPopScope2( onWillPop: () async => (await _mayExitPage(saving: false), null), diff --git a/packages/smooth_app/lib/pages/product/common/loading_status.dart b/packages/smooth_app/lib/pages/product/common/loading_status.dart new file mode 100644 index 00000000000..f16a9accd9a --- /dev/null +++ b/packages/smooth_app/lib/pages/product/common/loading_status.dart @@ -0,0 +1,6 @@ +/// Common loading status. +enum LoadingStatus { + LOADING, + LOADED, + ERROR, +} diff --git a/packages/smooth_app/lib/pages/product/common/product_dialog_helper.dart b/packages/smooth_app/lib/pages/product/common/product_dialog_helper.dart index c321e115b9b..1bbd5070b0d 100644 --- a/packages/smooth_app/lib/pages/product/common/product_dialog_helper.dart +++ b/packages/smooth_app/lib/pages/product/common/product_dialog_helper.dart @@ -60,7 +60,7 @@ class ProductDialogHelper { void _openProductNotFoundDialog() => showDialog( context: context, builder: (BuildContext context) { - final double availableWidth = MediaQuery.of(context).size.width - + final double availableWidth = MediaQuery.sizeOf(context).width - SmoothAlertDialog.defaultMargin.horizontal - SmoothAlertDialog.defaultContentPadding(context).horizontal; @@ -87,6 +87,7 @@ class ProductDialogHelper { SvgPicture.asset( 'assets/onboarding/birthday-cake.svg', package: AppHelper.APP_PACKAGE, + excludeFromSemantics: true, ), SizedBox(height: SMALL_SPACE * heightMultiplier), Text( @@ -101,26 +102,31 @@ class ProductDialogHelper { textAlign: TextAlign.center, ), SizedBox(height: MEDIUM_SPACE * heightMultiplier), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - flex: 4, - child: SvgCache( - unknownSvgNutriscore, - height: svgHeight, + Semantics( + label: appLocalizations + .new_product_dialog_illustration_description, + excludeSemantics: true, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 4, + child: SvgCache( + unknownSvgNutriscore, + height: svgHeight, + ), ), - ), - const Spacer(), - Expanded( - flex: 4, - child: SvgCache( - unknownSvgEcoscore, - height: svgHeight, + const Spacer(), + Expanded( + flex: 4, + child: SvgCache( + unknownSvgEcoscore, + height: svgHeight, + ), ), - ), - ], + ], + ), ), SizedBox(height: SMALL_SPACE * heightMultiplier), Text( @@ -134,9 +140,15 @@ class ProductDialogHelper { actionsAxis: Axis.vertical, positiveAction: SmoothActionButton( text: AppLocalizations.of(context).contribute, - onPressed: () => AppNavigator.of(context).push( - AppRoutes.PRODUCT_CREATOR(barcode), - ), + onPressed: () async { + await AppNavigator.of(context).push( + AppRoutes.PRODUCT_CREATOR(barcode), + ); + + if (context.mounted) { + Navigator.pop(context); + } + }, ), negativeAction: SmoothActionButton( text: AppLocalizations.of(context).close, @@ -155,13 +167,18 @@ class ProductDialogHelper { void _openErrorMessage(final String message) => showDialog( context: context, - builder: (BuildContext context) => SmoothAlertDialog( - body: getErrorMessage(message), - positiveAction: SmoothActionButton( - text: AppLocalizations.of(context).close, - onPressed: () => Navigator.pop(context), - ), - ), + builder: (BuildContext context) { + final AppLocalizations localizations = AppLocalizations.of(context); + + return SmoothAlertDialog( + title: localizations.product_internet_error_modal_title, + body: getErrorMessage(message), + positiveAction: SmoothActionButton( + text: localizations.close, + onPressed: () => Navigator.pop(context), + ), + ); + }, ); /// Opens an error dialog; to be used only if the status is not ok. @@ -173,7 +190,10 @@ class ProductDialogHelper { case FetchedProductStatus.userCancelled: return; case FetchedProductStatus.internetError: - _openErrorMessage(appLocalizations.product_internet_error); + _openErrorMessage( + appLocalizations.product_internet_error_modal_message( + fetchedProduct.exceptionString ?? '-'), + ); return; case FetchedProductStatus.internetNotFound: _openProductNotFoundDialog(); diff --git a/packages/smooth_app/lib/pages/product/common/product_list_item_simple.dart b/packages/smooth_app/lib/pages/product/common/product_list_item_simple.dart index efd699d6ed4..627644f194b 100644 --- a/packages/smooth_app/lib/pages/product/common/product_list_item_simple.dart +++ b/packages/smooth_app/lib/pages/product/common/product_list_item_simple.dart @@ -8,6 +8,7 @@ import 'package:smooth_app/cards/product_cards/smooth_product_card_template.dart import 'package:smooth_app/data_models/product_list.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/product/common/product_loading_status.dart'; import 'package:smooth_app/pages/product/common/product_model.dart'; import 'package:smooth_app/services/smooth_services.dart'; @@ -49,16 +50,16 @@ class _ProductListItemSimpleState extends State { context.watch(); _model.setLocalUpToDate(); switch (_model.loadingStatus) { - case LoadingStatus.LOADING: + case ProductLoadingStatus.LOADING: return SmoothProductCardTemplate( barcode: widget.barcode, ); - case LoadingStatus.DOWNLOADING: + case ProductLoadingStatus.DOWNLOADING: return SmoothProductCardTemplate( barcode: widget.barcode, message: appLocalizations.loading_dialog_default_title, ); - case LoadingStatus.LOADED: + case ProductLoadingStatus.LOADED: if (_model.product != null) { return SmoothProductCardFound( heroTag: @@ -70,7 +71,7 @@ class _ProductListItemSimpleState extends State { ); } break; - case LoadingStatus.ERROR: + case ProductLoadingStatus.ERROR: } Logs.w( 'product list item simple / could not load ${widget.barcode}', diff --git a/packages/smooth_app/lib/pages/product/common/product_list_page.dart b/packages/smooth_app/lib/pages/product/common/product_list_page.dart index cfb34da75af..59d9b4e671a 100644 --- a/packages/smooth_app/lib/pages/product/common/product_list_page.dart +++ b/packages/smooth_app/lib/pages/product/common/product_list_page.dart @@ -21,7 +21,6 @@ import 'package:smooth_app/generic_lib/widgets/smooth_responsive.dart'; import 'package:smooth_app/helpers/app_helper.dart'; import 'package:smooth_app/helpers/robotoff_insight_helper.dart'; import 'package:smooth_app/pages/all_product_list_modal.dart'; -import 'package:smooth_app/pages/carousel_manager.dart'; import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart'; import 'package:smooth_app/pages/product/common/product_list_item_popup_items.dart'; import 'package:smooth_app/pages/product/common/product_list_item_simple.dart'; @@ -29,6 +28,7 @@ import 'package:smooth_app/pages/product/common/product_list_popup_items.dart'; import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product_list_user_dialog_helper.dart'; +import 'package:smooth_app/pages/scan/carousel/scan_carousel_manager.dart'; import 'package:smooth_app/query/product_query.dart'; import 'package:smooth_app/widgets/smooth_app_bar.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; @@ -133,7 +133,7 @@ class _ProductListPageState extends State icon: const Icon(CupertinoIcons.barcode), label: Text(appLocalizations.product_list_empty_title), onPressed: () => - ExternalCarouselManager.read(context).showSearchCard(), + ExternalScanCarouselManager.read(context).showSearchCard(), ) : _selectionMode ? null @@ -261,13 +261,13 @@ class _ProductListPageState extends State SvgPicture.asset( 'assets/misc/empty-list.svg', package: AppHelper.APP_PACKAGE, - width: MediaQuery.of(context).size.width / 2, + width: MediaQuery.sizeOf(context).width / 2, ), Text( appLocalizations.product_list_empty_message, textAlign: TextAlign.center, style: themeData.textTheme.bodyMedium?.apply( - color: themeData.colorScheme.onBackground, + color: themeData.colorScheme.onSurface, ), ), EMPTY_WIDGET, diff --git a/packages/smooth_app/lib/pages/product/common/product_loading_status.dart b/packages/smooth_app/lib/pages/product/common/product_loading_status.dart new file mode 100644 index 00000000000..de4888f0ef3 --- /dev/null +++ b/packages/smooth_app/lib/pages/product/common/product_loading_status.dart @@ -0,0 +1,14 @@ +/// Product loading status. +enum ProductLoadingStatus { + /// Loading product from local database. + LOADING, + + /// Product loaded. + LOADED, + + /// Error. + ERROR, + + /// Downloading product from back-end. + DOWNLOADING, +} diff --git a/packages/smooth_app/lib/pages/product/common/product_model.dart b/packages/smooth_app/lib/pages/product/common/product_model.dart index 4895cdebf3f..3c825881564 100644 --- a/packages/smooth_app/lib/pages/product/common/product_model.dart +++ b/packages/smooth_app/lib/pages/product/common/product_model.dart @@ -4,23 +4,9 @@ import 'package:smooth_app/data_models/fetched_product.dart'; import 'package:smooth_app/database/dao_product.dart'; import 'package:smooth_app/database/dao_product_last_access.dart'; import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/pages/product/common/product_loading_status.dart'; import 'package:smooth_app/query/barcode_product_query.dart'; -/// Loading status. -enum LoadingStatus { - /// Loading product from local database. - LOADING, - - /// Product loaded. - LOADED, - - /// Error. - ERROR, - - /// Downloading product from back-end. - DOWNLOADING, -} - /// Model for product database get and refresh. class ProductModel with ChangeNotifier { /// In the constructor we retrieve async'ly the product from the local db. @@ -39,7 +25,7 @@ class ProductModel with ChangeNotifier { Product? _product; Product? get product => - _loadingStatus == LoadingStatus.LOADED ? _product : null; + _loadingStatus == ProductLoadingStatus.LOADED ? _product : null; /// Latest version of the product from the local database. /// @@ -51,9 +37,9 @@ class ProductModel with ChangeNotifier { Product? _databaseProduct; /// General loading status. - LoadingStatus _loadingStatus = LoadingStatus.LOADING; + ProductLoadingStatus _loadingStatus = ProductLoadingStatus.LOADING; - LoadingStatus get loadingStatus => _loadingStatus; + ProductLoadingStatus get loadingStatus => _loadingStatus; /// General loading error: a failing [FetchedProduct]. FetchedProduct? _loadingError; @@ -89,7 +75,7 @@ class ProductModel with ChangeNotifier { /// This is the printed error: /// A ProductModel was used after being disposed. /// Once you have called dispose() on a ProductModel, it can no longer be used. - void _safeNotifyListeners(final LoadingStatus status) { + void _safeNotifyListeners(final ProductLoadingStatus status) { try { _loadingStatus = status; notifyListeners(); @@ -103,7 +89,7 @@ class ProductModel with ChangeNotifier { _databaseProduct = await _daoProduct.get(barcode); if (_databaseProduct != null) { // found in the local database, perfect! - _safeNotifyListeners(LoadingStatus.LOADED); + _safeNotifyListeners(ProductLoadingStatus.LOADED); return; } if (localDatabase.upToDate.hasPendingChanges(barcode)) { @@ -111,7 +97,7 @@ class ProductModel with ChangeNotifier { // but with local not uploaded yet changes. // so we use a fake empty product instead. _databaseProduct = Product(barcode: barcode); - _safeNotifyListeners(LoadingStatus.LOADED); + _safeNotifyListeners(ProductLoadingStatus.LOADED); return; } // we need to download now! @@ -120,7 +106,7 @@ class ProductModel with ChangeNotifier { /// Downloads the product. To be used as a refresh after a network issue. Future download() async { - _safeNotifyListeners(LoadingStatus.DOWNLOADING); + _safeNotifyListeners(ProductLoadingStatus.DOWNLOADING); final FetchedProduct fetchedProduct = await BarcodeProductQuery( barcode: barcode, daoProduct: _daoProduct, @@ -128,10 +114,10 @@ class ProductModel with ChangeNotifier { ).getFetchedProduct(); if (fetchedProduct.status == FetchedProductStatus.ok) { _databaseProduct = fetchedProduct.product; - _safeNotifyListeners(LoadingStatus.LOADED); + _safeNotifyListeners(ProductLoadingStatus.LOADED); return; } _loadingError = fetchedProduct; - _safeNotifyListeners(LoadingStatus.ERROR); + _safeNotifyListeners(ProductLoadingStatus.ERROR); } } diff --git a/packages/smooth_app/lib/pages/product/common/product_query_page.dart b/packages/smooth_app/lib/pages/product/common/product_query_page.dart index 9ed060ae598..05e8a9ad268 100644 --- a/packages/smooth_app/lib/pages/product/common/product_query_page.dart +++ b/packages/smooth_app/lib/pages/product/common/product_query_page.dart @@ -21,14 +21,16 @@ import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_error_card.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/pages/personalized_ranking_page.dart'; +import 'package:smooth_app/pages/product/common/loading_status.dart'; import 'package:smooth_app/pages/product/common/product_list_item_simple.dart'; import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; +import 'package:smooth_app/pages/product/common/search_app_bar_title.dart'; +import 'package:smooth_app/pages/product/common/search_empty_screen.dart'; +import 'package:smooth_app/pages/product/common/search_loading_screen.dart'; import 'package:smooth_app/query/paged_product_query.dart'; -import 'package:smooth_app/resources/app_animations.dart'; import 'package:smooth_app/widgets/ranking_floating_action_button.dart'; import 'package:smooth_app/widgets/smooth_app_bar.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; -import 'package:smooth_app/widgets/smooth_text.dart'; class ProductQueryPage extends StatefulWidget { const ProductQueryPage({ @@ -94,7 +96,7 @@ class _ProductQueryPageState extends State @override Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); final ThemeData themeData = Theme.of(context); return ChangeNotifierProvider.value( @@ -112,7 +114,7 @@ class _ProductQueryPageState extends State ); case LoadingStatus.LOADING: if (_model.isEmpty()) { - return _LoadingScreen( + return SearchLoadingScreen( title: widget.name, ); } @@ -120,7 +122,7 @@ class _ProductQueryPageState extends State case LoadingStatus.LOADED: if (_model.isEmpty()) { // TODO(monsieurtanuki): should be tracked as well, shouldn't it? - return _EmptyScreen( + return SearchEmptyScreen( name: widget.name, emptiness: _getEmptyText( themeData, @@ -156,12 +158,9 @@ class _ProductQueryPageState extends State SmoothSharedAnimationController( child: SmoothScaffold( floatingActionButton: Row( - mainAxisAlignment: _showBackToTopButton - ? MainAxisAlignment.spaceBetween - : MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), + Expanded( child: RankingFloatingActionButton( onPressed: () => Navigator.push( context, @@ -187,15 +186,24 @@ class _ProductQueryPageState extends State padding: const EdgeInsetsDirectional.only( start: SMALL_SPACE, ), - child: FloatingActionButton( - backgroundColor: themeData.colorScheme.secondary, - onPressed: () { - _scrollToTop(); - }, - tooltip: appLocalizations.go_back_to_top, - child: Icon( - Icons.arrow_upward, - color: themeData.colorScheme.onSecondary, + child: SizedBox( + height: MINIMUM_TOUCH_SIZE, + child: ElevatedButton( + onPressed: () { + _scrollToTop(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: themeData.colorScheme.secondary, + foregroundColor: themeData.colorScheme.onSecondary, + shape: const CircleBorder(), + padding: EdgeInsets.zero, + ), + child: Center( + child: Icon( + Icons.arrow_upward, + color: themeData.colorScheme.onSecondary, + ), + ), ), ), ), @@ -209,7 +217,7 @@ class _ProductQueryPageState extends State elevation: 2, automaticallyImplyLeading: false, leading: const SmoothBackButton(), - title: _AppBarTitle( + title: SearchAppBarTitle( title: widget.searchResult ? widget.name : appLocalizations.product_search_same_category, @@ -256,7 +264,7 @@ class _ProductQueryPageState extends State return const Center(child: CircularProgressIndicator()); } return SizedBox( - height: MediaQuery.of(context).size.height / 4, + height: MediaQuery.sizeOf(context).height / 4, ); } return ProductListItemSimple( @@ -288,7 +296,7 @@ class _ProductQueryPageState extends State final ThemeData themeData, final String errorMessage, ) { - return _EmptyScreen( + return SearchEmptyScreen( name: widget.name, emptiness: Padding( padding: const EdgeInsets.all(SMALL_SPACE), @@ -491,75 +499,6 @@ class _ProductQueryPageState extends State } } -class _EmptyScreen extends StatelessWidget { - const _EmptyScreen({ - required this.name, - required this.emptiness, - this.actions, - Key? key, - }) : super(key: key); - - final String name; - final Widget emptiness; - final List? actions; - - @override - Widget build(BuildContext context) { - return SmoothScaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - leading: const SmoothBackButton(), - title: _AppBarTitle( - title: name, - editableAppBarTitle: false, - ), - actions: actions, - ), - body: Center(child: emptiness), - ); - } -} - -class _AppBarTitle extends StatelessWidget { - const _AppBarTitle({ - required this.title, - this.multiLines = true, - required this.editableAppBarTitle, - Key? key, - }) : super(key: key); - - final String title; - final bool multiLines; - final bool editableAppBarTitle; - - @override - Widget build(BuildContext context) { - final Widget child = Text( - title, - maxLines: multiLines ? 2 : 1, - ); - - if (editableAppBarTitle) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - - return InkWell( - onTap: () { - Navigator.of(context).pop(ProductQueryPageResult.editProductQuery); - }, - child: Tooltip( - message: appLocalizations.tap_to_edit_search, - child: SizedBox( - width: double.infinity, - child: child, - ), - ), - ); - } else { - return child; - } - } -} - // TODO(monsieurtanki): put it in a specific reusable class class _Action { _Action({ @@ -572,43 +511,3 @@ class _Action { final String text; final VoidCallback onPressed; } - -enum ProductQueryPageResult { - editProductQuery, - unknown, -} - -class _LoadingScreen extends StatelessWidget { - const _LoadingScreen({ - required this.title, - }); - - final String title; - - @override - Widget build(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - - return _EmptyScreen( - name: title, - emptiness: FractionallySizedBox( - widthFactor: 0.75, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SearchEyeAnimation( - size: MediaQuery.sizeOf(context).width * 0.2, - ), - const SizedBox(height: VERY_LARGE_SPACE * 2), - TextHighlighter( - text: appLocalizations.product_search_loading_message(title), - filter: title, - softWrap: true, - textAlign: TextAlign.center, - ), - ], - ), - ), - ); - } -} diff --git a/packages/smooth_app/lib/pages/product/common/product_query_page_helper.dart b/packages/smooth_app/lib/pages/product/common/product_query_page_helper.dart index 5d7b0a02d68..fd4cb0e10ec 100644 --- a/packages/smooth_app/lib/pages/product/common/product_query_page_helper.dart +++ b/packages/smooth_app/lib/pages/product/common/product_query_page_helper.dart @@ -4,10 +4,9 @@ import 'package:smooth_app/data_models/product_list.dart'; import 'package:smooth_app/data_models/product_list_supplier.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/pages/product/common/product_query_page.dart'; +import 'package:smooth_app/pages/product/common/search_helper.dart'; import 'package:smooth_app/query/paged_product_query.dart'; -typedef EditProductQueryCallback = void Function(String productName); - class ProductQueryPageHelper { Future openBestChoice({ required final PagedProductQuery productQuery, @@ -16,7 +15,7 @@ class ProductQueryPageHelper { required final BuildContext context, bool editableAppBarTitle = true, bool searchResult = true, - EditProductQueryCallback? editQueryCallback, + SearchQueryCallback? editQueryCallback, }) async { final ProductListSupplier supplier = await ProductListSupplier.getBestSupplier( @@ -26,10 +25,9 @@ class ProductQueryPageHelper { if (!context.mounted) { return; } - final ProductQueryPageResult? result = - await Navigator.push( + final bool? result = await Navigator.push( context, - MaterialPageRoute( + MaterialPageRoute( builder: (BuildContext context) => ProductQueryPage( productListSupplier: supplier, name: name, @@ -39,32 +37,53 @@ class ProductQueryPageHelper { ), ); - if (result == ProductQueryPageResult.editProductQuery) { + if (result == true) { editQueryCallback?.call(name); } } static String getDurationStringFromSeconds( - final int seconds, AppLocalizations appLocalizations) { + final int seconds, + final AppLocalizations appLocalizations, { + final bool compact = false, + }) { final double minutes = seconds / 60; final int roundMinutes = minutes.round(); + if (roundMinutes == 0) { + // TODO(monsieurtanuki): localize if relevant + if (compact) { + return '${seconds}s'; + } + } if (roundMinutes < 60) { + if (compact) { + return '${roundMinutes}m'; + } return appLocalizations.plural_ago_minutes(roundMinutes); } final double hours = minutes / 60; final int roundHours = hours.round(); if (roundHours < 24) { + if (compact) { + return '${roundHours}h'; + } return appLocalizations.plural_ago_hours(roundHours); } final double days = hours / 24; final int roundDays = days.round(); if (roundDays < 7) { + if (compact) { + return '${roundDays}d'; + } return appLocalizations.plural_ago_days(roundDays); } final double weeks = days / 7; final int roundWeeks = weeks.round(); + if (compact) { + return '${roundWeeks}w'; + } if (roundWeeks <= 4) { return appLocalizations.plural_ago_weeks(roundWeeks); } @@ -75,10 +94,17 @@ class ProductQueryPageHelper { } static String getDurationStringFromTimestamp( - final int timestamp, BuildContext context) { + final int timestamp, + final BuildContext context, { + final bool compact = false, + }) { final int now = LocalDatabase.nowInMillis(); final int seconds = ((now - timestamp) / 1000).floor(); - return getDurationStringFromSeconds(seconds, AppLocalizations.of(context)); + return getDurationStringFromSeconds( + seconds, + AppLocalizations.of(context), + compact: compact, + ); } static String getProductListLabel( diff --git a/packages/smooth_app/lib/pages/product/common/product_refresher.dart b/packages/smooth_app/lib/pages/product/common/product_refresher.dart index c681527a01b..d3c4f54b07e 100644 --- a/packages/smooth_app/lib/pages/product/common/product_refresher.dart +++ b/packages/smooth_app/lib/pages/product/common/product_refresher.dart @@ -37,7 +37,7 @@ class ProductRefresher { children: [ SvgPicture.asset( 'assets/onboarding/globe.svg', - height: MediaQuery.of(context).size.height * .5, + height: MediaQuery.sizeOf(context).height * .5, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 25), @@ -178,9 +178,9 @@ class ProductRefresher { return const FetchedProduct.internetNotFound(); } catch (e) { Logs.e('Refresh from server error', ex: e); - final List connectivityResults = + final ConnectivityResult connectivityResult = await Connectivity().checkConnectivity(); - if (connectivityResults.contains(ConnectivityResult.none)) { + if (connectivityResult == ConnectivityResult.none) { return FetchedProduct.error( exceptionString: e.toString(), isConnected: false, diff --git a/packages/smooth_app/lib/pages/product/common/search_app_bar_title.dart b/packages/smooth_app/lib/pages/product/common/search_app_bar_title.dart new file mode 100644 index 00000000000..ecafb80ca15 --- /dev/null +++ b/packages/smooth_app/lib/pages/product/common/search_app_bar_title.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +/// Common search app bar title. +class SearchAppBarTitle extends StatelessWidget { + const SearchAppBarTitle({ + required this.title, + this.multiLines = true, + required this.editableAppBarTitle, + Key? key, + }) : super(key: key); + + final String title; + final bool multiLines; + final bool editableAppBarTitle; + + @override + Widget build(BuildContext context) { + final Widget child = Text( + title, + maxLines: multiLines ? 2 : 1, + ); + + if (editableAppBarTitle) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return InkWell( + onTap: () => Navigator.of(context).pop(true), + child: Tooltip( + message: appLocalizations.tap_to_edit_search, + child: SizedBox( + width: double.infinity, + child: child, + ), + ), + ); + } + return child; + } +} diff --git a/packages/smooth_app/lib/pages/product/common/search_empty_screen.dart b/packages/smooth_app/lib/pages/product/common/search_empty_screen.dart new file mode 100644 index 00000000000..2ab31bed112 --- /dev/null +++ b/packages/smooth_app/lib/pages/product/common/search_empty_screen.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; +import 'package:smooth_app/pages/product/common/search_app_bar_title.dart'; +import 'package:smooth_app/widgets/smooth_scaffold.dart'; + +class SearchEmptyScreen extends StatelessWidget { + const SearchEmptyScreen({ + required this.name, + required this.emptiness, + this.actions, + Key? key, + }) : super(key: key); + + final String name; + final Widget emptiness; + final List? actions; + + @override + Widget build(BuildContext context) { + return SmoothScaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + leading: const SmoothBackButton(), + title: SearchAppBarTitle( + title: name, + editableAppBarTitle: false, + ), + actions: actions, + ), + body: Center(child: emptiness), + ); + } +} diff --git a/packages/smooth_app/lib/pages/product/common/search_helper.dart b/packages/smooth_app/lib/pages/product/common/search_helper.dart new file mode 100644 index 00000000000..e3313f562c4 --- /dev/null +++ b/packages/smooth_app/lib/pages/product/common/search_helper.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/database/dao_string_list.dart'; +import 'package:smooth_app/database/local_database.dart'; + +typedef SearchQueryCallback = void Function(String query); + +/// Common "text-field + history" search helper. +abstract class SearchHelper { + const SearchHelper(); + + /// Action to perform for a search. + @protected + void search( + BuildContext context, + String query, { + required SearchQueryCallback searchQueryCallback, + }); + + /// Key for [DaoStringList], used to store the latest X queries. + @protected + String get historyKey; + + /// Hint text for the search field. + String getHintText(final AppLocalizations appLocalizations); + + /// Returns all the previous queries, in reverse order. + List getAllQueries(final LocalDatabase localDatabase) => + DaoStringList(localDatabase).getAll(historyKey).reversed.toList(); + + /// Adds a query to the history. + Future addQuery( + final LocalDatabase localDatabase, + final String query, + ) async => + DaoStringList(localDatabase).add(historyKey, query); + + /// Removes a query from the history. + Future removeQuery( + final LocalDatabase localDatabase, + final String query, + ) async => + DaoStringList(localDatabase).remove(historyKey, query); + + /// Typical search when we have a controller+focus. + void searchWithController( + BuildContext context, + String query, + TextEditingController controller, + FocusNode focusNode, + ) => + search( + context, + query, + searchQueryCallback: (String query) { + controller.text = query; + focusNode.requestFocus(); + }, + ); +} diff --git a/packages/smooth_app/lib/pages/product/common/search_loading_screen.dart b/packages/smooth_app/lib/pages/product/common/search_loading_screen.dart new file mode 100644 index 00000000000..d04fd382412 --- /dev/null +++ b/packages/smooth_app/lib/pages/product/common/search_loading_screen.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/product/common/search_empty_screen.dart'; +import 'package:smooth_app/resources/app_animations.dart'; +import 'package:smooth_app/widgets/smooth_text.dart'; + +/// Common search loading screen. +class SearchLoadingScreen extends StatelessWidget { + const SearchLoadingScreen({ + required this.title, + }); + + final String title; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return SearchEmptyScreen( + name: title, + emptiness: FractionallySizedBox( + widthFactor: 0.75, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SearchEyeAnimation( + size: MediaQuery.sizeOf(context).width * 0.2, + ), + const SizedBox(height: VERY_LARGE_SPACE * 2), + TextHighlighter( + text: appLocalizations.product_search_loading_message(title), + filter: title, + softWrap: true, + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/product/common/search_preloaded_item.dart b/packages/smooth_app/lib/pages/product/common/search_preloaded_item.dart new file mode 100644 index 00000000000..926f6b8ff1d --- /dev/null +++ b/packages/smooth_app/lib/pages/product/common/search_preloaded_item.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +/// Common search preloaded list item. +abstract class SearchPreloadedItem { + /// Displays the list item. + Widget getWidget( + final BuildContext context, { + final VoidCallback? onDismissItem, + }); + + /// Deletes this item from the preload list. + Future delete(final BuildContext context); +} diff --git a/packages/smooth_app/lib/pages/product/compare_products3_page.dart b/packages/smooth_app/lib/pages/product/compare_products3_page.dart index 5e5041d6e56..56b967f430a 100644 --- a/packages/smooth_app/lib/pages/product/compare_products3_page.dart +++ b/packages/smooth_app/lib/pages/product/compare_products3_page.dart @@ -104,7 +104,7 @@ class _CompareProducts3PageState extends State { final List brands = []; final List quantities = []; final List pictures = []; - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); for (final Product product in widget.products) { names.add(getProductName(product, appLocalizations)); brands.add(getProductBrands(product, appLocalizations)); diff --git a/packages/smooth_app/lib/pages/product/edit_image_button.dart b/packages/smooth_app/lib/pages/product/edit_image_button.dart index e7ab6df11e3..dd30c53526f 100644 --- a/packages/smooth_app/lib/pages/product/edit_image_button.dart +++ b/packages/smooth_app/lib/pages/product/edit_image_button.dart @@ -9,12 +9,21 @@ class EditImageButton extends StatelessWidget { required this.label, required this.onPressed, this.borderWidth, - }); + }) : _centerContent = false; + + /// Centered version of the button. + const EditImageButton.center({ + required this.iconData, + required this.label, + required this.onPressed, + this.borderWidth, + }) : _centerContent = true; final IconData iconData; final String label; final VoidCallback onPressed; final double? borderWidth; + final bool _centerContent; @override Widget build(BuildContext context) { @@ -24,22 +33,30 @@ class EditImageButton extends StatelessWidget { child: OutlinedButton.icon( icon: Icon(iconData), style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(colorScheme.onPrimary), - shape: MaterialStateProperty.all( + backgroundColor: WidgetStateProperty.all(colorScheme.onPrimary), + shape: WidgetStateProperty.all( const RoundedRectangleBorder(borderRadius: ROUNDED_BORDER_RADIUS), ), side: borderWidth == null ? null - : MaterialStateBorderSide.resolveWith( + : WidgetStateBorderSide.resolveWith( (_) => BorderSide( color: colorScheme.primary, width: borderWidth!, ), ), + padding: _centerContent + ? WidgetStateProperty.all( + const EdgeInsets.symmetric( + vertical: LARGE_SPACE, + ), + ) + : null, + alignment: _centerContent ? AlignmentDirectional.center : null, ), onPressed: onPressed, label: SizedBox( - width: double.infinity, + width: !_centerContent ? double.infinity : null, child: Padding( padding: EdgeInsets.all(borderWidth ?? 0), child: AutoSizeText( diff --git a/packages/smooth_app/lib/pages/product/edit_new_packagings.dart b/packages/smooth_app/lib/pages/product/edit_new_packagings.dart index 6c4e248066c..4c0a003929b 100644 --- a/packages/smooth_app/lib/pages/product/edit_new_packagings.dart +++ b/packages/smooth_app/lib/pages/product/edit_new_packagings.dart @@ -324,7 +324,7 @@ Color _getSmoothCardColorAlternate(final BuildContext context, int index) { if (index.isOdd) { cardColor = PRIMARY_GREY_COLOR; } else { - cardColor = darkColorScheme.background; + cardColor = darkColorScheme.surface; } } diff --git a/packages/smooth_app/lib/pages/product/edit_ocr/edit_ocr_main_action.dart b/packages/smooth_app/lib/pages/product/edit_ocr/edit_ocr_main_action.dart new file mode 100644 index 00000000000..6fa71cb85e7 --- /dev/null +++ b/packages/smooth_app/lib/pages/product/edit_ocr/edit_ocr_main_action.dart @@ -0,0 +1,294 @@ +part of 'edit_ocr_page.dart'; + +class _EditOcrMainAction extends StatelessWidget { + const _EditOcrMainAction({ + required this.onPressed, + required this.helper, + required this.state, + }); + + final VoidCallback onPressed; + final OcrHelper helper; + final _OcrState state; + + @override + Widget build(BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + final Widget? child = switch (state) { + _OcrState.IMAGE_LOADING => _EditOcrActionLoadingContent( + helper: helper, + appLocalizations: appLocalizations, + ), + _OcrState.IMAGE_LOADED => _ExtractMainActionContentLoaded( + helper: helper, + appLocalizations: appLocalizations, + onPressed: onPressed, + ), + _OcrState.EXTRACTING_DATA => _EditOcrActionExtractingContent( + helper: helper, + appLocalizations: appLocalizations, + ), + _OcrState.OTHER => null, + }; + + if (child == null) { + return EMPTY_WIDGET; + } + + final SmoothColorsThemeExtension theme = + Theme.of(context).extension()!; + + return SizedBox( + height: 45.0 * (_computeFontScaleFactor(context)), + width: double.infinity, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: ANGULAR_BORDER_RADIUS, + color: theme.primarySemiDark, + border: Border.all( + color: theme.primaryBlack, + width: 2.0, + ), + ), + child: Material( + type: MaterialType.transparency, + child: ProgressIndicatorTheme( + data: const ProgressIndicatorThemeData( + color: Colors.white, + ), + child: IconTheme( + data: const IconThemeData(color: Colors.white), + child: DefaultTextStyle( + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15.5, + color: Colors.white, + ), + child: child, + ), + ), + ), + ), + ), + ); + } + + double _computeFontScaleFactor(BuildContext context) { + final double fontSize = DefaultTextStyle.of(context).style.fontSize ?? 15.0; + final double scaledFontSize = + MediaQuery.textScalerOf(context).scale(fontSize); + + return scaledFontSize / fontSize; + } +} + +class _EditOcrActionExtractingContent extends StatelessWidget { + const _EditOcrActionExtractingContent({ + required this.helper, + required this.appLocalizations, + }); + + final OcrHelper helper; + final AppLocalizations appLocalizations; + + @override + Widget build(BuildContext context) { + return Semantics( + label: helper.getActionLoadingPhoto(appLocalizations), + excludeSemantics: true, + child: Shimmer( + gradient: const LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Colors.black, + Colors.white, + Colors.black, + ], + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const _ExtractMainActionProgressIndicator(), + Expanded( + child: Text( + helper.getActionExtractingData(appLocalizations), + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ), + ); + } +} + +class _ExtractMainActionContentLoaded extends StatelessWidget { + const _ExtractMainActionContentLoaded({ + required this.helper, + required this.appLocalizations, + required this.onPressed, + }); + + final OcrHelper helper; + final AppLocalizations appLocalizations; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + return Semantics( + excludeSemantics: true, + value: helper.getActionExtractText(appLocalizations), + button: true, + child: InkWell( + onTap: onPressed, + borderRadius: ANGULAR_BORDER_RADIUS, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Text( + helper.getActionExtractText(appLocalizations), + ), + ), + const Icon( + Icons.download, + semanticLabel: '', + ), + ], + ), + ), + ), + ); + } +} + +class _EditOcrActionLoadingContent extends StatelessWidget { + const _EditOcrActionLoadingContent({ + required this.helper, + required this.appLocalizations, + }); + + final OcrHelper helper; + final AppLocalizations appLocalizations; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.only( + start: MEDIUM_SPACE, + end: VERY_SMALL_SPACE, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const _ExtractMainActionProgressIndicator(), + Expanded( + child: Text( + helper.getActionLoadingPhoto(appLocalizations), + ), + ), + AspectRatio( + aspectRatio: 1.0, + child: InkWell( + onTap: () => _openExplanation(context), + borderRadius: ANGULAR_BORDER_RADIUS, + child: Icon( + Icons.info_outline, + semanticLabel: helper.getActionLoadingPhotoDialogTitle( + appLocalizations, + ), + ), + ), + ) + ], + ), + ); + } + + void _openExplanation(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + + return SmoothAlertDialog( + title: helper.getActionLoadingPhotoDialogTitle( + appLocalizations, + ), + leadingTitle: const Icon( + Icons.info_outline, + semanticLabel: '', + ), + close: true, + body: Text( + helper.getActionLoadingPhotoDialogBody( + appLocalizations, + ), + ), + positiveAction: SmoothActionButton( + text: appLocalizations.okay, + onPressed: () => Navigator.pop(context), + ), + ); + }, + ); + } +} + +/// We use a custom progress indicator, because Material and Cupertino Widgets +/// don't have the same size. +class _ExtractMainActionProgressIndicator extends StatelessWidget { + const _ExtractMainActionProgressIndicator(); + + @override + Widget build(BuildContext context) { + if (Platform.isIOS || Platform.isMacOS) { + return const Padding( + padding: EdgeInsetsDirectional.only( + start: SMALL_SPACE, + end: MEDIUM_SPACE, + top: SMALL_SPACE, + bottom: SMALL_SPACE, + ), + child: CupertinoActivityIndicator( + radius: 10.0, + color: Colors.white, + ), + ); + } + + return const Padding( + padding: EdgeInsetsDirectional.only( + start: SMALL_SPACE, + end: MEDIUM_SPACE, + top: MEDIUM_SPACE, + bottom: MEDIUM_SPACE, + ), + child: AspectRatio( + aspectRatio: 1.0, + child: CircularProgressIndicator( + strokeWidth: 2.5, + valueColor: AlwaysStoppedAnimation(Colors.white), + // backgroundColor: Colors.white, + ), + ), + ); + } +} + +enum _OcrState { + IMAGE_LOADING, + IMAGE_LOADED, + EXTRACTING_DATA, + OTHER, +} diff --git a/packages/smooth_app/lib/pages/product/edit_ocr_page.dart b/packages/smooth_app/lib/pages/product/edit_ocr/edit_ocr_page.dart similarity index 65% rename from packages/smooth_app/lib/pages/product/edit_ocr_page.dart rename to packages/smooth_app/lib/pages/product/edit_ocr/edit_ocr_page.dart index 8dd7233bf33..d78a52cce5e 100644 --- a/packages/smooth_app/lib/pages/product/edit_ocr_page.dart +++ b/packages/smooth_app/lib/pages/product/edit_ocr/edit_ocr_page.dart @@ -1,8 +1,13 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; +import 'package:shimmer/shimmer.dart'; import 'package:smooth_app/background/background_task_details.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/up_to_date_mixin.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/transient_file.dart'; @@ -12,14 +17,20 @@ import 'package:smooth_app/generic_lib/loading_dialog.dart'; import 'package:smooth_app/generic_lib/widgets/picture_not_found.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/helpers/provider_helper.dart'; import 'package:smooth_app/pages/image_crop_page.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; +import 'package:smooth_app/pages/product/edit_ocr/ocr_helper.dart'; import 'package:smooth_app/pages/product/explanation_widget.dart'; import 'package:smooth_app/pages/product/multilingual_helper.dart'; -import 'package:smooth_app/pages/product/ocr_helper.dart'; import 'package:smooth_app/pages/product/product_image_button.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; +import 'package:smooth_app/themes/theme_provider.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; +part 'edit_ocr_main_action.dart'; + /// Editing with OCR a product field and the corresponding image. /// /// Typical use-cases: ingredients and packaging. @@ -43,6 +54,7 @@ class _EditOcrPageState extends State with UpToDateMixin { late final MultilingualHelper _multilingualHelper; OcrHelper get _helper => widget.helper; + bool _extractingData = false; @override void initState() { @@ -62,7 +74,8 @@ class _EditOcrPageState extends State with UpToDateMixin { /// /// When done, populates the related page field. Future _extractData() async { - // TODO(monsieurtanuki): hide the "extract" button while extracting, or display a loading dialog on top + setState(() => _extractingData = true); + try { final String? extractedText = await _helper.getExtractedText( widget.product, @@ -83,8 +96,9 @@ class _EditOcrPageState extends State with UpToDateMixin { if (_controller.text != extractedText) { setState(() => _controller.text = extractedText); } - } catch (e) { - // + } catch (_) { + } finally { + setState(() => _extractingData = false); } } @@ -144,11 +158,16 @@ class _EditOcrPageState extends State with UpToDateMixin { ); } - Widget _getImageButton(final ProductImageButtonType type) => Padding( + Widget _getImageButton( + final ProductImageButtonType type, + final bool imageExists, + ) => + Padding( padding: const EdgeInsets.symmetric(horizontal: SMALL_SPACE), child: type.getButton( product: upToDateProduct, imageField: _helper.getImageField(), + imageExists: imageExists, language: _multilingualHelper.getCurrentLanguage(), isLoggedInMandatory: widget.isLoggedInMandatory, borderWidth: 2, @@ -156,7 +175,7 @@ class _EditOcrPageState extends State with UpToDateMixin { ); Widget _getImageWidget(final TransientFile transientFile) { - final Size size = MediaQuery.of(context).size; + final Size size = MediaQuery.sizeOf(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); final ImageProvider? imageProvider = transientFile.getImageProvider(); @@ -210,6 +229,8 @@ class _EditOcrPageState extends State with UpToDateMixin { final OpenFoodFactsLanguage language = _multilingualHelper.getCurrentLanguage(); final ImageProvider? imageProvider = transientFile.getImageProvider(); + final bool imageExists = imageProvider != null; + return Align( alignment: AlignmentDirectional.bottomStart, child: Column( @@ -232,10 +253,16 @@ class _EditOcrPageState extends State with UpToDateMixin { mainAxisSize: MainAxisSize.max, children: [ Expanded( - child: _getImageButton(ProductImageButtonType.server), + child: _getImageButton( + ProductImageButtonType.server, + imageExists, + ), ), Expanded( - child: _getImageButton(ProductImageButtonType.local), + child: _getImageButton( + ProductImageButtonType.local, + imageExists, + ), ), ], ), @@ -246,11 +273,16 @@ class _EditOcrPageState extends State with UpToDateMixin { mainAxisSize: MainAxisSize.max, children: [ Expanded( - child: - _getImageButton(ProductImageButtonType.unselect), + child: _getImageButton( + ProductImageButtonType.unselect, + imageExists, + ), ), Expanded( - child: _getImageButton(ProductImageButtonType.edit), + child: _getImageButton( + ProductImageButtonType.edit, + imageExists, + ), ), ], ), @@ -260,16 +292,21 @@ class _EditOcrPageState extends State with UpToDateMixin { ), Flexible( flex: 1, - child: Container( + child: DecoratedBox( decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: const BorderRadiusDirectional.only( - topStart: ANGULAR_RADIUS, - topEnd: ANGULAR_RADIUS, - )), + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadiusDirectional.only( + topStart: ANGULAR_RADIUS, + topEnd: ANGULAR_RADIUS, + ), + ), child: SingleChildScrollView( child: Padding( - padding: const EdgeInsets.all(LARGE_SPACE), + padding: const EdgeInsetsDirectional.only( + start: LARGE_SPACE, + end: LARGE_SPACE, + top: LARGE_SPACE, + ), child: Column( children: [ if (!_multilingualHelper.isMonolingual()) @@ -277,29 +314,60 @@ class _EditOcrPageState extends State with UpToDateMixin { setState: setState, product: upToDateProduct, ), - if (transientFile.isServerImage()) - SmoothActionButtonsBar.single( - action: SmoothActionButton( - text: - _helper.getActionExtractText(appLocalizations), - onPressed: () async => _extractData(), - ), - ) - else if (transientFile.isImageAvailable()) - // TODO(monsieurtanuki): what if slow upload? text instead? - const CircularProgressIndicator.adaptive(), + _EditOcrMainAction( + onPressed: _extractData, + helper: _helper, + state: _extractState(transientFile), + ), const SizedBox(height: MEDIUM_SPACE), - TextField( - controller: _controller, - decoration: InputDecoration( - fillColor: Colors.white.withOpacity(0.2), - filled: true, - enabledBorder: const OutlineInputBorder( - borderRadius: ANGULAR_BORDER_RADIUS, - ), - ), - maxLines: null, - textInputAction: TextInputAction.newline, + ConsumerFilter( + buildWhen: ( + UserPreferences? previousValue, + UserPreferences currentValue, + ) { + return previousValue?.getFlag(UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr) != + currentValue.getFlag(UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr); + }, + builder: ( + BuildContext context, + UserPreferences prefs, + Widget? child, + ) { + final ThemeData theme = Theme.of(context); + + return Theme( + data: theme.copyWith( + colorScheme: theme.colorScheme.copyWith( + onSurface: context + .read() + .isDarkMode(context) + ? Colors.white + : Colors.black, + ), + ), + child: TextField( + controller: _controller, + decoration: InputDecoration( + fillColor: Colors.white.withOpacity(0.2), + filled: true, + enabledBorder: const OutlineInputBorder( + borderRadius: ANGULAR_BORDER_RADIUS, + ), + ), + maxLines: null, + textInputAction: TextInputAction.newline, + spellCheckConfiguration: (prefs.getFlag( + UserPreferencesDevMode + .userPreferencesFlagSpellCheckerOnOcr) ?? + false) && + (Platform.isAndroid || Platform.isIOS) + ? const SpellCheckConfiguration() + : const SpellCheckConfiguration.disabled(), + ), + ); + }, ), const SizedBox(height: SMALL_SPACE), ExplanationWidget( @@ -321,31 +389,30 @@ class _EditOcrPageState extends State with UpToDateMixin { iconData: Icons.add_a_photo, ), ), - const SizedBox(height: MEDIUM_SPACE), - SmoothActionButtonsBar( - axis: Axis.horizontal, - negativeAction: SmoothActionButton( - text: appLocalizations.cancel, - onPressed: () => Navigator.pop(context), - ), - positiveAction: SmoothActionButton( - text: appLocalizations.save, - onPressed: () async { - await _updateText(); - if (!mounted) { - return; - } - Navigator.pop(context); - }, - ), - ), - const SizedBox(height: MEDIUM_SPACE), ], ), ), ), ), ), + SmoothActionButtonsBar( + axis: Axis.horizontal, + negativeAction: SmoothActionButton( + text: appLocalizations.cancel, + onPressed: () => Navigator.pop(context), + ), + positiveAction: SmoothActionButton( + text: appLocalizations.save, + onPressed: () async { + await _updateText(); + if (!mounted) { + return; + } + Navigator.pop(context); + }, + ), + ), + SizedBox(height: MediaQuery.paddingOf(context).bottom), ], ), ); @@ -373,4 +440,16 @@ class _EditOcrPageState extends State with UpToDateMixin { } return result; } + + _OcrState _extractState(TransientFile transientFile) { + if (_extractingData) { + return _OcrState.EXTRACTING_DATA; + } else if (transientFile.isServerImage()) { + return _OcrState.IMAGE_LOADED; + } else if (transientFile.isImageAvailable()) { + return _OcrState.IMAGE_LOADING; + } else { + return _OcrState.OTHER; + } + } } diff --git a/packages/smooth_app/lib/pages/product/ocr_helper.dart b/packages/smooth_app/lib/pages/product/edit_ocr/ocr_helper.dart similarity index 81% rename from packages/smooth_app/lib/pages/product/ocr_helper.dart rename to packages/smooth_app/lib/pages/product/edit_ocr/ocr_helper.dart index 61b5b2cc33c..f8cdc759b71 100644 --- a/packages/smooth_app/lib/pages/product/ocr_helper.dart +++ b/packages/smooth_app/lib/pages/product/edit_ocr/ocr_helper.dart @@ -39,6 +39,20 @@ abstract class OcrHelper { /// Returns the "extract text" button label String getActionExtractText(final AppLocalizations appLocalizations); + /// Returns the "loading photo" button label + String getActionLoadingPhoto(final AppLocalizations appLocalizations); + + /// Returns the title of the dialog to explain the "loading photo" state + String getActionLoadingPhotoDialogTitle( + final AppLocalizations appLocalizations); + + /// Returns the content of the dialog to explain the "loading photo" state + String getActionLoadingPhotoDialogBody( + final AppLocalizations appLocalizations); + + /// Returns the "Extracting data…" button label + String getActionExtractingData(final AppLocalizations appLocalizations); + /// Returns the "refresh photo" button label String getActionRefreshPhoto(final AppLocalizations appLocalizations); diff --git a/packages/smooth_app/lib/pages/product/ocr_ingredients_helper.dart b/packages/smooth_app/lib/pages/product/edit_ocr/ocr_ingredients_helper.dart similarity index 77% rename from packages/smooth_app/lib/pages/product/ocr_ingredients_helper.dart rename to packages/smooth_app/lib/pages/product/edit_ocr/ocr_ingredients_helper.dart index 6f1eeb8a48d..d2720d98b57 100644 --- a/packages/smooth_app/lib/pages/product/ocr_ingredients_helper.dart +++ b/packages/smooth_app/lib/pages/product/edit_ocr/ocr_ingredients_helper.dart @@ -2,7 +2,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/background/background_task_details.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; -import 'package:smooth_app/pages/product/ocr_helper.dart'; +import 'package:smooth_app/pages/product/edit_ocr/ocr_helper.dart'; import 'package:smooth_app/query/product_query.dart'; /// OCR Helper for ingredients. @@ -43,7 +43,23 @@ class OcrIngredientsHelper extends OcrHelper { @override String getActionExtractText(final AppLocalizations appLocalizations) => - appLocalizations.edit_ingredients_extrait_ingredients_btn_text; + appLocalizations.edit_ingredients_extract_ingredients_btn_text; + + @override + String getActionExtractingData(AppLocalizations appLocalizations) => + appLocalizations.edit_ingredients_extracting_ingredients_btn_text; + + @override + String getActionLoadingPhoto(AppLocalizations appLocalizations) => + appLocalizations.edit_ingredients_loading_photo_btn_text; + + @override + String getActionLoadingPhotoDialogTitle(AppLocalizations appLocalizations) => + appLocalizations.edit_ingredients_loading_photo_help_dialog_title; + + @override + String getActionLoadingPhotoDialogBody(AppLocalizations appLocalizations) => + appLocalizations.edit_ingredients_loading_photo_help_dialog_body; @override String getActionRefreshPhoto(final AppLocalizations appLocalizations) => diff --git a/packages/smooth_app/lib/pages/product/ocr_packaging_helper.dart b/packages/smooth_app/lib/pages/product/edit_ocr/ocr_packaging_helper.dart similarity index 80% rename from packages/smooth_app/lib/pages/product/ocr_packaging_helper.dart rename to packages/smooth_app/lib/pages/product/edit_ocr/ocr_packaging_helper.dart index 179adefe296..6590f15a993 100644 --- a/packages/smooth_app/lib/pages/product/ocr_packaging_helper.dart +++ b/packages/smooth_app/lib/pages/product/edit_ocr/ocr_packaging_helper.dart @@ -4,7 +4,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:smooth_app/background/background_task_details.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; -import 'package:smooth_app/pages/product/ocr_helper.dart'; +import 'package:smooth_app/pages/product/edit_ocr/ocr_helper.dart'; import 'package:smooth_app/query/product_query.dart'; /// OCR Helper for packaging. @@ -48,6 +48,22 @@ class OcrPackagingHelper extends OcrHelper { String getActionExtractText(final AppLocalizations appLocalizations) => appLocalizations.edit_packaging_extract_btn_text; + @override + String getActionExtractingData(AppLocalizations appLocalizations) => + appLocalizations.edit_packaging_extracting_btn_text; + + @override + String getActionLoadingPhoto(AppLocalizations appLocalizations) => + appLocalizations.edit_packaging_loading_photo_btn_text; + + @override + String getActionLoadingPhotoDialogTitle(AppLocalizations appLocalizations) => + appLocalizations.edit_packaging_loading_photo_help_dialog_title; + + @override + String getActionLoadingPhotoDialogBody(AppLocalizations appLocalizations) => + appLocalizations.edit_packaging_loading_photo_help_dialog_body; + @override String getActionRefreshPhoto(final AppLocalizations appLocalizations) => appLocalizations.edit_packaging_refresh_photo_btn_text; diff --git a/packages/smooth_app/lib/pages/product/edit_product_page.dart b/packages/smooth_app/lib/pages/product/edit_product_page.dart index 66c9895a4eb..83d542fafc9 100644 --- a/packages/smooth_app/lib/pages/product/edit_product_page.dart +++ b/packages/smooth_app/lib/pages/product/edit_product_page.dart @@ -13,6 +13,8 @@ import 'package:smooth_app/generic_lib/widgets/smooth_list_tile_card.dart'; import 'package:smooth_app/generic_lib/widgets/svg_icon.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/pages/prices/price_meta_product.dart'; +import 'package:smooth_app/pages/prices/product_price_add_page.dart'; import 'package:smooth_app/pages/product/add_other_details_page.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/nutrition_page_loaded.dart'; @@ -74,8 +76,9 @@ class _EditProductPageState extends State with UpToDateMixin { theme.textTheme.titleLarge?.fontSize?.clamp(13.0, 17.0) ?? 13.0, maxLines: !_barcodeVisibleInAppbar ? 2 : 1, - style: theme.textTheme.titleLarge - ?.copyWith(fontWeight: FontWeight.w500), + style: theme.textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w500, + ), ), if (barcode.isNotEmpty) AnimatedContainer( @@ -85,6 +88,7 @@ class _EditProductPageState extends State with UpToDateMixin { barcode, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.normal, + height: 0.9, ), ), ), @@ -253,6 +257,15 @@ class _EditProductPageState extends State with UpToDateMixin { ); }, ), + _ListTitleItem( + title: appLocalizations.prices_add_a_price, + leading: const Icon(Icons.add), + onTap: () async => ProductPriceAddPage.showProductPage( + context: context, + product: PriceMetaProduct.product(upToDateProduct), + proofType: ProofType.priceTag, + ), + ), ], ), ), @@ -377,7 +390,7 @@ class _ProductBarcodeState extends State<_ProductBarcode> { Widget build(BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); final Brightness brightness = Theme.of(context).brightness; - final Size screenSize = MediaQuery.of(context).size; + final Size screenSize = MediaQuery.sizeOf(context); return BarcodeWidget( padding: EdgeInsets.symmetric( diff --git a/packages/smooth_app/lib/pages/product/may_exit_page_helper.dart b/packages/smooth_app/lib/pages/product/may_exit_page_helper.dart index cae566a60d7..7fd27780312 100644 --- a/packages/smooth_app/lib/pages/product/may_exit_page_helper.dart +++ b/packages/smooth_app/lib/pages/product/may_exit_page_helper.dart @@ -10,7 +10,10 @@ class MayExitPageHelper { /// * `null` means the user's dismissed the dialog and doesn't want to leave. /// * `true` means the user wants to save the changes and leave. /// * `false` means the user wants to ignore the changes and leave. - Future openSaveBeforeLeavingDialog(final BuildContext context) async => + Future openSaveBeforeLeavingDialog( + final BuildContext context, { + final String? title, + }) async => showDialog( context: context, builder: (final BuildContext context) { @@ -21,7 +24,7 @@ class MayExitPageHelper { actionsAxis: Axis.vertical, body: Text(appLocalizations.edit_product_form_item_exit_confirmation), - title: appLocalizations.edit_product_label, + title: title ?? appLocalizations.edit_product_label, negativeAction: SmoothActionButton( text: appLocalizations .edit_product_form_item_exit_confirmation_negative_button, diff --git a/packages/smooth_app/lib/pages/product/new_product_page.dart b/packages/smooth_app/lib/pages/product/new_product_page.dart index 76245c04f67..e20901edfa2 100644 --- a/packages/smooth_app/lib/pages/product/new_product_page.dart +++ b/packages/smooth_app/lib/pages/product/new_product_page.dart @@ -1,6 +1,5 @@ import 'package:assorted_layout_widgets/assorted_layout_widgets.dart'; import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -22,9 +21,8 @@ import 'package:smooth_app/generic_lib/duration_constants.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart'; import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; import 'package:smooth_app/helpers/analytics_helper.dart'; -import 'package:smooth_app/helpers/launch_url_helper.dart'; -import 'package:smooth_app/pages/carousel_manager.dart'; import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart'; +import 'package:smooth_app/pages/prices/prices_card.dart'; import 'package:smooth_app/pages/product/common/product_list_page.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/edit_product_page.dart'; @@ -35,6 +33,7 @@ import 'package:smooth_app/pages/product/standard_knowledge_panel_cards.dart'; import 'package:smooth_app/pages/product/summary_card.dart'; import 'package:smooth_app/pages/product/website_card.dart'; import 'package:smooth_app/pages/product_list_user_dialog_helper.dart'; +import 'package:smooth_app/pages/scan/carousel/scan_carousel_manager.dart'; import 'package:smooth_app/query/product_query.dart'; import 'package:smooth_app/themes/constant_icons.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; @@ -86,8 +85,8 @@ class _ProductPageState extends State @override Widget build(BuildContext context) { - final ExternalCarouselManagerState carouselManager = - ExternalCarouselManager.read(context); + final ExternalScanCarouselManagerState carouselManager = + ExternalScanCarouselManager.read(context); carouselManager.currentBarcode = barcode; final ThemeData themeData = Theme.of(context); _productPreferences = context.watch(); @@ -218,7 +217,6 @@ class _ProductPageState extends State upToDateProduct, _productPreferences, isFullVersion: true, - showUnansweredQuestions: true, ), ), ), @@ -239,16 +237,7 @@ class _ProductPageState extends State if (upToDateProduct.website != null && upToDateProduct.website!.trim().isNotEmpty) WebsiteCard(upToDateProduct.website!), - Padding( - padding: const EdgeInsets.all(SMALL_SPACE), - child: SmoothLargeButtonWithIcon( - text: appLocalizations.prices_app_button, - icon: CupertinoIcons.tag_fill, - onPressed: () async => LaunchUrlHelper.launchURL( - 'https://prices.openfoodfacts.org/app/products/${upToDateProduct.barcode!}', - ), - ), - ), + PricesCard(upToDateProduct), if (userPreferences.getFlag( UserPreferencesDevMode.userPreferencesFlagUserOrderedKP) ?? false) @@ -269,6 +258,8 @@ class _ProductPageState extends State if (questionsLayout == ProductQuestionsLayout.banner) // assuming it's tall enough in order to go above the banner const SizedBox(height: 4 * VERY_LARGE_SPACE), + // Space for the navigation bar + SizedBox(height: MediaQuery.paddingOf(context).bottom), ], ), ); @@ -425,11 +416,11 @@ class _ProductPageState extends State ), child: ElevatedButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( const EdgeInsets.symmetric( horizontal: VERY_LARGE_SPACE, vertical: MEDIUM_SPACE), ), - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: ROUNDED_BORDER_RADIUS, ), diff --git a/packages/smooth_app/lib/pages/product/nutrition_add_nutrient_button.dart b/packages/smooth_app/lib/pages/product/nutrition_add_nutrient_button.dart index 86b1a5dea6a..afa30d2b80d 100644 --- a/packages/smooth_app/lib/pages/product/nutrition_add_nutrient_button.dart +++ b/packages/smooth_app/lib/pages/product/nutrition_add_nutrient_button.dart @@ -99,7 +99,7 @@ class NutritionAddNutrientButton extends StatelessWidget { } }, style: ButtonStyle( - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: ROUNDED_BORDER_RADIUS, side: BorderSide.none, 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 3a872cbbf39..6fed13681ec 100644 --- a/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart +++ b/packages/smooth_app/lib/pages/product/nutrition_page_loaded.dart @@ -322,7 +322,7 @@ class _NutritionPageLoadedState extends State value: _nutritionContainer.noNutritionData, onChanged: (final bool value) => setState(() => _nutritionContainer.noNutritionData = value), - trackColor: MaterialStateProperty.all( + trackColor: WidgetStateProperty.all( Theme.of(context).colorScheme.onPrimary), ), ), diff --git a/packages/smooth_app/lib/pages/product/product_field_editor.dart b/packages/smooth_app/lib/pages/product/product_field_editor.dart index 6c4f415b926..55b7c4717e4 100644 --- a/packages/smooth_app/lib/pages/product/product_field_editor.dart +++ b/packages/smooth_app/lib/pages/product/product_field_editor.dart @@ -5,11 +5,11 @@ import 'package:smooth_app/helpers/analytics_helper.dart'; import 'package:smooth_app/pages/product/add_basic_details_page.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/edit_new_packagings.dart'; -import 'package:smooth_app/pages/product/edit_ocr_page.dart'; +import 'package:smooth_app/pages/product/edit_ocr/edit_ocr_page.dart'; +import 'package:smooth_app/pages/product/edit_ocr/ocr_helper.dart'; +import 'package:smooth_app/pages/product/edit_ocr/ocr_ingredients_helper.dart'; +import 'package:smooth_app/pages/product/edit_ocr/ocr_packaging_helper.dart'; import 'package:smooth_app/pages/product/nutrition_page_loaded.dart'; -import 'package:smooth_app/pages/product/ocr_helper.dart'; -import 'package:smooth_app/pages/product/ocr_ingredients_helper.dart'; -import 'package:smooth_app/pages/product/ocr_packaging_helper.dart'; import 'package:smooth_app/pages/product/simple_input_page.dart'; import 'package:smooth_app/pages/product/simple_input_page_helpers.dart'; diff --git a/packages/smooth_app/lib/pages/product/product_image_button.dart b/packages/smooth_app/lib/pages/product/product_image_button.dart index f977ce74f05..2c6352d9f24 100644 --- a/packages/smooth_app/lib/pages/product/product_image_button.dart +++ b/packages/smooth_app/lib/pages/product/product_image_button.dart @@ -65,6 +65,7 @@ enum ProductImageButtonType { required final OpenFoodFactsLanguage language, required final bool isLoggedInMandatory, final double? borderWidth, + required bool imageExists, }) => switch (this) { ProductImageButtonType.local => ProductImageLocalButton( @@ -73,6 +74,7 @@ enum ProductImageButtonType { language: language, isLoggedInMandatory: isLoggedInMandatory, borderWidth: borderWidth, + imageExists: imageExists, ), ProductImageButtonType.server => ProductImageServerButton( product: product, diff --git a/packages/smooth_app/lib/pages/product/product_image_crop_button.dart b/packages/smooth_app/lib/pages/product/product_image_crop_button.dart index 15e4126e82b..165a07c4485 100644 --- a/packages/smooth_app/lib/pages/product/product_image_crop_button.dart +++ b/packages/smooth_app/lib/pages/product/product_image_crop_button.dart @@ -11,9 +11,12 @@ import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/transient_file.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; import 'package:smooth_app/pages/crop_page.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; import 'package:smooth_app/pages/image_crop_page.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/product_image_button.dart'; +import 'package:smooth_app/pages/product_crop_helper.dart'; +import 'package:smooth_app/query/product_query.dart'; /// Product Image Button editing the current image. class ProductImageCropButton extends ProductImageButton { @@ -50,7 +53,7 @@ class ProductImageCropButton extends ProductImageButton { if (productImage != null) { final int? imageId = int.tryParse(productImage.imgid!); if (imageId != null) { - await _openEditCroppedImage(context, imageId, productImage); + await _openCropAgainPage(context, imageId, productImage); return; } } @@ -58,7 +61,7 @@ class ProductImageCropButton extends ProductImageButton { // alternate option: use the transient file. File? imageFile = _transientFile.getImage(); if (imageFile != null) { - await _openCropPage(navigatorState, imageFile); + await _openCropNewPage(navigatorState, imageFile); return; } @@ -72,36 +75,12 @@ class ProductImageCropButton extends ProductImageButton { ); } if (imageFile != null) { - await _openCropPage(navigatorState, imageFile); + await _openCropNewPage(navigatorState, imageFile); return; } } - Future _openCropPage( - final NavigatorState navigatorState, - final File imageFile, { - final int? imageId, - final Rect? initialCropRect, - final CropRotation? initialRotation, - }) async => - navigatorState.push( - MaterialPageRoute( - builder: (BuildContext context) => CropPage( - language: language, - barcode: barcode, - imageField: _imageData.imageField, - inputFile: imageFile, - imageId: imageId, - initiallyDifferent: false, - initialCropRect: initialCropRect, - initialRotation: initialRotation, - isLoggedInMandatory: isLoggedInMandatory, - ), - fullscreenDialog: true, - ), - ); - - Future _openEditCroppedImage( + Future _openCropAgainPage( final BuildContext context, final int imageId, final ProductImage productImage, @@ -113,23 +92,57 @@ class ProductImageCropButton extends ProductImageButton { ProductImage.raw( imgid: imageId.toString(), size: ImageSize.ORIGINAL, - ).getUrl(barcode), + ).getUrl( + barcode, + uriHelper: ProductQuery.uriProductHelper, + ), DaoInt(localDatabase), ); if (imageFile == null) { return null; } - return _openCropPage( - navigatorState, - imageFile, - imageId: imageId, - initialCropRect: _getCropRect(productImage), - initialRotation: CropRotationExtension.fromDegrees( - productImage.angle?.degree ?? 0, + return navigatorState.push( + MaterialPageRoute( + builder: (BuildContext context) => CropPage( + inputFile: imageFile, + initiallyDifferent: false, + initialCropRect: _getCropRect(productImage), + initialRotation: CropRotationExtension.fromDegrees( + productImage.angle?.degree ?? 0, + ), + isLoggedInMandatory: isLoggedInMandatory, + cropHelper: ProductCropAgainHelper( + language: language, + barcode: barcode, + imageField: _imageData.imageField, + imageId: imageId, + ), + ), + fullscreenDialog: true, ), ); } + Future _openCropNewPage( + final NavigatorState navigatorState, + final File imageFile, + ) async => + navigatorState.push( + MaterialPageRoute( + builder: (BuildContext context) => CropPage( + inputFile: imageFile, + initiallyDifferent: false, + isLoggedInMandatory: isLoggedInMandatory, + cropHelper: ProductCropNewHelper( + language: language, + barcode: barcode, + imageField: _imageData.imageField, + ), + ), + fullscreenDialog: true, + ), + ); + ProductImage? _getBestProductImage() { if (product.images == null) { return null; diff --git a/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart b/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart index eb923c6fb3f..26197d46234 100644 --- a/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart +++ b/packages/smooth_app/lib/pages/product/product_image_gallery_view.dart @@ -17,6 +17,7 @@ import 'package:smooth_app/pages/image_crop_page.dart'; import 'package:smooth_app/pages/product/common/product_refresher.dart'; import 'package:smooth_app/pages/product/product_image_swipeable_view.dart'; import 'package:smooth_app/query/product_query.dart'; +import 'package:smooth_app/resources/app_animations.dart'; import 'package:smooth_app/widgets/slivers.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; @@ -190,6 +191,8 @@ class _PhotoRow extends StatelessWidget { @override Widget build(BuildContext context) { final ImageField imageField = _getImageField(position); + final TransientFile transientFile = _getTransientFile(imageField); + return Padding( padding: const EdgeInsets.only( top: SMALL_SPACE, @@ -201,12 +204,24 @@ class _PhotoRow extends StatelessWidget { ), child: Column( children: [ - AspectRatio( - aspectRatio: 1.0, - child: SmoothImage( - rounded: false, - imageProvider: _getTransientFile(imageField).getImageProvider(), - ), + Stack( + children: [ + AspectRatio( + aspectRatio: 1.0, + child: SmoothImage( + rounded: false, + imageProvider: transientFile.getImageProvider(), + ), + ), + if (transientFile.isImageAvailable() && + !transientFile.isServerImage()) + Positioned.directional( + textDirection: Directionality.of(context), + bottom: VERY_SMALL_SPACE, + end: VERY_SMALL_SPACE, + child: const CloudUploadAnimation.circle(size: 30.0), + ), + ], ), Expanded( child: Center( diff --git a/packages/smooth_app/lib/pages/product/product_image_local_button.dart b/packages/smooth_app/lib/pages/product/product_image_local_button.dart index 4f4a687db3b..049cffa4291 100644 --- a/packages/smooth_app/lib/pages/product/product_image_local_button.dart +++ b/packages/smooth_app/lib/pages/product/product_image_local_button.dart @@ -11,15 +11,19 @@ class ProductImageLocalButton extends ProductImageButton { required super.imageField, required super.language, required super.isLoggedInMandatory, + required this.imageExists, super.borderWidth, }); + final bool imageExists; + @override IconData getIconData() => Icons.add_a_photo; @override - String getLabel(final AppLocalizations appLocalizations) => - appLocalizations.capture; + String getLabel(final AppLocalizations appLocalizations) => imageExists + ? appLocalizations.capture + : appLocalizations.capture_new_picture; @override Future action(final BuildContext context) async { diff --git a/packages/smooth_app/lib/pages/product/product_image_viewer.dart b/packages/smooth_app/lib/pages/product/product_image_viewer.dart index c41b8bb3b42..f92e8ece55c 100644 --- a/packages/smooth_app/lib/pages/product/product_image_viewer.dart +++ b/packages/smooth_app/lib/pages/product/product_image_viewer.dart @@ -8,10 +8,13 @@ import 'package:smooth_app/data_models/up_to_date_mixin.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/database/transient_file.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/duration_constants.dart'; import 'package:smooth_app/generic_lib/widgets/language_selector.dart'; import 'package:smooth_app/generic_lib/widgets/picture_not_found.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; +import 'package:smooth_app/pages/image_crop_page.dart'; import 'package:smooth_app/pages/product/product_image_button.dart'; +import 'package:smooth_app/resources/app_animations.dart'; /// Displays a full-screen image with an "edit" floating button. class ProductImageViewer extends StatefulWidget { @@ -43,11 +46,16 @@ class _ProductImageViewerState extends State initUpToDate(widget.product, context.read()); } - Widget _getImageButton(final ProductImageButtonType type) => Padding( + Widget _getImageButton( + final ProductImageButtonType type, + final bool imageExists, + ) => + Padding( padding: const EdgeInsets.symmetric(horizontal: SMALL_SPACE), child: type.getButton( product: upToDateProduct, imageField: widget.imageField, + imageExists: imageExists, language: widget.language, isLoggedInMandatory: widget.isLoggedInMandatory, ), @@ -63,7 +71,11 @@ class _ProductImageViewerState extends State widget.imageField, widget.language, ); - final ImageProvider? imageProvider = _getTransientFile().getImageProvider(); + final TransientFile transientFile = _getTransientFile(); + final ImageProvider? imageProvider = transientFile.getImageProvider(); + final bool imageExists = imageProvider != null; + final bool isLoading = + transientFile.isImageAvailable() && !transientFile.isServerImage(); final Iterable selectedLanguages = getProductImageLanguages( upToDateProduct, @@ -96,42 +108,80 @@ class _ProductImageViewerState extends State textAlign: TextAlign.center, ), ), + Positioned.fill( + child: Material( + type: MaterialType.transparency, + child: Semantics( + label: appLocalizations.take_photo_title, + child: InkWell( + onTap: () async { + await confirmAndUploadNewPicture( + context, + imageField: widget.imageField, + barcode: barcode, + language: widget.language, + isLoggedInMandatory: true, + ); + }, + ), + ), + ), + ), ], ) - : PhotoView( - minScale: 0.2, - imageProvider: imageProvider, - heroAttributes: PhotoViewHeroAttributes( - tag: 'photo_${widget.imageField.offTag}', - flightShuttleBuilder: ( - _, - Animation animation, - HeroFlightDirection flightDirection, - BuildContext fromHeroContext, - BuildContext toHeroContext, - ) { - return AnimatedBuilder( - animation: animation, - builder: (_, __) { - Widget widget; - if (flightDirection == - HeroFlightDirection.push) { - widget = fromHeroContext.widget; - } else { - widget = toHeroContext.widget; - } + : SizedBox.expand( + child: Stack( + children: [ + Positioned.fill( + child: AnimatedOpacity( + opacity: isLoading ? 0.5 : 1.0, + duration: SmoothAnimationsDuration.short, + child: PhotoView( + minScale: 0.2, + imageProvider: imageProvider, + heroAttributes: PhotoViewHeroAttributes( + tag: 'photo_${widget.imageField.offTag}', + flightShuttleBuilder: ( + _, + Animation animation, + HeroFlightDirection flightDirection, + BuildContext fromHeroContext, + BuildContext toHeroContext, + ) { + return AnimatedBuilder( + animation: animation, + builder: (_, __) { + Widget widget; + if (flightDirection == + HeroFlightDirection.push) { + widget = fromHeroContext.widget; + } else { + widget = toHeroContext.widget; + } - return ClipRRect( - borderRadius: BorderRadius.circular( - 1 - animation.value) * - ROUNDED_RADIUS.x, - child: widget, - ); - }, - ); - }), - backgroundDecoration: const BoxDecoration( - color: Colors.black, + return ClipRRect( + borderRadius: BorderRadius.circular( + 1 - animation.value) * + ROUNDED_RADIUS.x, + child: widget, + ); + }, + ); + }), + backgroundDecoration: const BoxDecoration( + color: Colors.black, + ), + ), + ), + ), + if (isLoading) + Center( + child: CloudUploadAnimation.circle( + size: MediaQuery.sizeOf(context).longestSide * + 0.2, + ), + ), + ], ), ), ), @@ -173,10 +223,16 @@ class _ProductImageViewerState extends State mainAxisSize: MainAxisSize.max, children: [ Expanded( - child: _getImageButton(ProductImageButtonType.server), + child: _getImageButton( + ProductImageButtonType.server, + imageExists, + ), ), Expanded( - child: _getImageButton(ProductImageButtonType.local), + child: _getImageButton( + ProductImageButtonType.local, + imageExists, + ), ), ], ), @@ -187,10 +243,16 @@ class _ProductImageViewerState extends State mainAxisSize: MainAxisSize.max, children: [ Expanded( - child: _getImageButton(ProductImageButtonType.unselect), + child: _getImageButton( + ProductImageButtonType.unselect, + imageExists, + ), ), Expanded( - child: _getImageButton(ProductImageButtonType.edit), + child: _getImageButton( + ProductImageButtonType.edit, + imageExists, + ), ), ], ), diff --git a/packages/smooth_app/lib/pages/product/product_incomplete_card.dart b/packages/smooth_app/lib/pages/product/product_incomplete_card.dart index ceba846b333..c60c7897639 100644 --- a/packages/smooth_app/lib/pages/product/product_incomplete_card.dart +++ b/packages/smooth_app/lib/pages/product/product_incomplete_card.dart @@ -111,13 +111,13 @@ class ProductIncompleteCard extends StatelessWidget { ), ), style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( + backgroundColor: WidgetStateProperty.all( colorScheme.primary, ), - foregroundColor: MaterialStateProperty.all( + foregroundColor: WidgetStateProperty.all( colorScheme.onPrimary, ), - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( const RoundedRectangleBorder(borderRadius: ANGULAR_BORDER_RADIUS), ), ), diff --git a/packages/smooth_app/lib/pages/product/product_question_answers_options.dart b/packages/smooth_app/lib/pages/product/product_question_answers_options.dart index bd708b337da..d9e1c31e72a 100644 --- a/packages/smooth_app/lib/pages/product/product_question_answers_options.dart +++ b/packages/smooth_app/lib/pages/product/product_question_answers_options.dart @@ -22,7 +22,7 @@ class ProductQuestionAnswersOptions extends StatelessWidget { const Color yesNoTextColor = Colors.white; final Color maybeTextColor = Colors.grey.shade700; - final double yesNoHeight = MediaQuery.of(context).size.width / (6); + final double yesNoHeight = MediaQuery.sizeOf(context).width / (6); return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -91,7 +91,7 @@ class ProductQuestionAnswersOptions extends StatelessWidget { child: TextButton( onPressed: () => onAnswer(insightAnnotation), style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(backgroundColor), + backgroundColor: WidgetStateProperty.all(backgroundColor), ), child: Text( buttonText, diff --git a/packages/smooth_app/lib/pages/product/summary_card.dart b/packages/smooth_app/lib/pages/product/summary_card.dart index 2a430b6e974..6245291f22e 100644 --- a/packages/smooth_app/lib/pages/product/summary_card.dart +++ b/packages/smooth_app/lib/pages/product/summary_card.dart @@ -17,6 +17,7 @@ import 'package:smooth_app/helpers/product_cards_helper.dart'; import 'package:smooth_app/helpers/ui_helpers.dart'; import 'package:smooth_app/knowledge_panel/knowledge_panels/knowledge_panel_page.dart'; import 'package:smooth_app/knowledge_panel/knowledge_panels_builder.dart'; +import 'package:smooth_app/pages/hunger_games/question_card.dart'; import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; import 'package:smooth_app/pages/product/hideable_container.dart'; import 'package:smooth_app/pages/product/product_compatibility_header.dart'; @@ -41,7 +42,7 @@ class SummaryCard extends StatefulWidget { this._product, this._productPreferences, { this.isFullVersion = false, - this.showUnansweredQuestions = false, + this.showQuestionsBanner = false, this.isRemovable = true, this.isSettingVisible = true, this.isProductEditable = true, @@ -58,9 +59,8 @@ class SummaryCard extends StatefulWidget { /// Buttons should only be visible in full mode final bool isFullVersion; - /// If true, the summary card will try to load unanswered questions about this - /// product and give a prompt to answer those questions. - final bool showUnansweredQuestions; + /// If true, show the [QuestionCard] if there are questions for the product. + final bool showQuestionsBanner; /// If true, there will be a button to remove the product from the carousel. final bool isRemovable; @@ -347,6 +347,7 @@ class _SummaryCardState extends State with UpToDateMixin { ProductIncompleteCard(product: upToDateProduct), ..._getAttributes(scoreAttributes), if (widget.isFullVersion && + widget.showQuestionsBanner && _questionsLayout == ProductQuestionsLayout.button) ProductQuestionsWidget( upToDateProduct, diff --git a/packages/smooth_app/lib/pages/product/website_card.dart b/packages/smooth_app/lib/pages/product/website_card.dart index 1f2a42bc671..28f3d400db0 100644 --- a/packages/smooth_app/lib/pages/product/website_card.dart +++ b/packages/smooth_app/lib/pages/product/website_card.dart @@ -12,45 +12,53 @@ class WebsiteCard extends StatelessWidget { @override Widget build(BuildContext context) { + final AppLocalizations localizations = AppLocalizations.of(context); final String website = _getWebsite(); + return buildProductSmoothCard( - body: InkWell( - onTap: () async => LaunchUrlHelper.launchURL(website), - borderRadius: ROUNDED_BORDER_RADIUS, - child: Container( - width: double.infinity, - padding: const EdgeInsetsDirectional.only( - start: LARGE_SPACE, - top: LARGE_SPACE, - bottom: LARGE_SPACE, - // To be perfectly aligned with arrows - end: 21.0, - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - AppLocalizations.of(context).product_field_website_title, - style: Theme.of(context).textTheme.displaySmall, - ), - const SizedBox(height: SMALL_SPACE), - Text( - website, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(color: Colors.blue), - ), - ], + body: Semantics( + label: localizations.product_field_website_title, + value: Uri.parse(website).host, + link: true, + excludeSemantics: true, + child: InkWell( + onTap: () async => LaunchUrlHelper.launchURL(website), + borderRadius: ROUNDED_BORDER_RADIUS, + child: Container( + width: double.infinity, + padding: const EdgeInsetsDirectional.only( + start: LARGE_SPACE, + top: LARGE_SPACE, + bottom: LARGE_SPACE, + // To be perfectly aligned with arrows + end: 21.0, + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + localizations.product_field_website_title, + style: Theme.of(context).textTheme.displaySmall, + ), + const SizedBox(height: SMALL_SPACE), + Text( + website, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.blue), + ), + ], + ), ), - ), - const Icon(Icons.open_in_new), - ], + const Icon(Icons.open_in_new), + ], + ), ), ), ), diff --git a/packages/smooth_app/lib/pages/product_crop_helper.dart b/packages/smooth_app/lib/pages/product_crop_helper.dart new file mode 100644 index 00000000000..956949330c1 --- /dev/null +++ b/packages/smooth_app/lib/pages/product_crop_helper.dart @@ -0,0 +1,232 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui' as ui; + +import 'package:crop_image/crop_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/background/background_task_crop.dart'; +import 'package:smooth_app/background/background_task_image.dart'; +import 'package:smooth_app/data_models/continuous_scan_model.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/helpers/image_field_extension.dart'; +import 'package:smooth_app/pages/crop_helper.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; + +/// Crop Helper for product images. +abstract class ProductCropHelper extends CropHelper { + ProductCropHelper({ + required this.imageField, + required this.language, + required this.barcode, + }); + + final ImageField imageField; + final OpenFoodFactsLanguage language; + final String barcode; + + @override + String getPageTitle(final AppLocalizations appLocalizations) => + imageField.getImagePageTitle(appLocalizations); + + @override + IconData getProcessIcon() => Icons.send; + + @override + String getProcessLabel(final AppLocalizations appLocalizations) => + appLocalizations.send_image_button_label; + + @override + bool get enableEraser => false; + + @protected + Future refresh(final BuildContext context) async { + final LocalDatabase localDatabase = context.read(); + localDatabase.notifyListeners(); + final ContinuousScanModel model = context.read(); + await model.onCreateProduct(barcode); // TODO(monsieurtanuki): a bit fishy + } +} + +/// Crop Helper for product images: brand new image. +class ProductCropNewHelper extends ProductCropHelper { + ProductCropNewHelper({ + required super.imageField, + required super.language, + required super.barcode, + }); + + @override + bool isNewImage() => true; + + @override + Future process({ + required final BuildContext context, + required final CropController controller, + required final ui.Image image, + required final File inputFile, + required final File smallCroppedFile, + required final Directory directory, + required final int sequenceNumber, + final List? offsets, + }) async { + // in this case, it's a brand new picture, with crop parameters. + // for performance reasons, we do not crop the image full-size here, + // but in the background task. + // for privacy reasons, we won't send the full image to the server and + // let it crop it: we'll send the cropped image directly. + final File fullFile = await copyFullImageFile( + directory, + sequenceNumber, + inputFile, + ); + final Rect cropRect = getLocalCropRect(controller); + if (!context.mounted) { + return null; + } + await BackgroundTaskImage.addTask( + barcode, + language: language, + imageField: imageField, + fullFile: fullFile, + croppedFile: smallCroppedFile, + rotation: controller.rotation.degrees, + x1: cropRect.left.ceil(), + y1: cropRect.top.ceil(), + x2: cropRect.right.floor(), + y2: cropRect.bottom.floor(), + context: context, + ); + + if (context.mounted) { + await refresh(context); + } + return getCropParameters( + controller: controller, + fullFile: fullFile, + smallCroppedFile: smallCroppedFile, + ); + } +} + +/// Crop Helper for product images: from an existing image. +class ProductCropAgainHelper extends ProductCropHelper { + ProductCropAgainHelper({ + required super.imageField, + required super.language, + required super.barcode, + required this.imageId, + }); + + final int imageId; + + @override + bool isNewImage() => false; + + @override + Future process({ + required final BuildContext context, + required final CropController controller, + required final ui.Image image, + required final File inputFile, + required final File smallCroppedFile, + required final Directory directory, + required final int sequenceNumber, + final List? offsets, + }) async { + // in this case, it's an existing picture, with crop parameters. + // we let the server do everything: better performance, and no privacy + // issue here (we're cropping from an allegedly already privacy compliant + // picture). + final Rect cropRect = _getServerCropRect(controller, image); + await BackgroundTaskCrop.addTask( + barcode, + language: language, + imageField: imageField, + imageId: imageId, + croppedFile: smallCroppedFile, + rotation: controller.rotation.degrees, + x1: cropRect.left.ceil(), + y1: cropRect.top.ceil(), + x2: cropRect.right.floor(), + y2: cropRect.bottom.floor(), + context: context, + ); + if (context.mounted) { + await refresh(context); + } + return getCropParameters( + controller: controller, + fullFile: null, + smallCroppedFile: smallCroppedFile, + ); + } + + /// Returns the crop rect according to server cropping method. + Rect _getServerCropRect( + final CropController controller, + final ui.Image image, + ) { + final Offset center = _getRotatedOffsetForOff( + controller.crop.center, + controller, + image, + ); + final Offset topLeft = _getRotatedOffsetForOff( + controller.crop.topLeft, + controller, + image, + ); + double width = 2 * (center.dx - topLeft.dx); + if (width < 0) { + width = -width; + } + double height = 2 * (center.dy - topLeft.dy); + if (height < 0) { + height = -height; + } + final Rect rect = Rect.fromCenter( + center: center, + width: width, + height: height, + ); + return rect; + } + + Offset _getRotatedOffsetForOff( + final Offset offset, + final CropController controller, + final ui.Image image, + ) => + _getRotatedOffsetForOffHelper( + controller.rotation, + offset, + image.width.toDouble(), + image.height.toDouble(), + ); + + /// Returns the offset as rotated, for the OFF-dart rotation/crop tool. + Offset _getRotatedOffsetForOffHelper( + final CropRotation rotation, + final Offset offset01, + final double noonWidth, + final double noonHeight, + ) { + switch (rotation) { + case CropRotation.up: + case CropRotation.down: + return Offset( + noonWidth * offset01.dx, + noonHeight * offset01.dy, + ); + case CropRotation.right: + case CropRotation.left: + return Offset( + noonHeight * offset01.dx, + noonWidth * offset01.dy, + ); + } + } +} diff --git a/packages/smooth_app/lib/pages/proof_crop_helper.dart b/packages/smooth_app/lib/pages/proof_crop_helper.dart new file mode 100644 index 00000000000..90abe490906 --- /dev/null +++ b/packages/smooth_app/lib/pages/proof_crop_helper.dart @@ -0,0 +1,73 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui' as ui; + +import 'package:crop_image/crop_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:smooth_app/pages/crop_helper.dart'; +import 'package:smooth_app/pages/crop_parameters.dart'; +import 'package:smooth_app/pages/prices/price_model.dart'; + +/// Crop Helper for proof images: brand new image. +class ProofCropHelper extends CropHelper { + ProofCropHelper({ + required this.model, + }); + + final PriceModel model; + + @override + bool isNewImage() => true; + + @override + String getPageTitle(final AppLocalizations appLocalizations) => + switch (model.proofType) { + ProofType.receipt => appLocalizations.prices_proof_receipt, + ProofType.priceTag => appLocalizations.prices_proof_price_tag, + _ => 'unexpected' + }; + + @override + IconData getProcessIcon() => Icons.check; + + @override + String getProcessLabel(final AppLocalizations appLocalizations) => + appLocalizations.okay; + + @override + bool get enableEraser => true; + + @override + Future process({ + required final BuildContext context, + required final CropController controller, + required final ui.Image image, + required final File inputFile, + required final File smallCroppedFile, + required final Directory directory, + required final int sequenceNumber, + final List? offsets, + }) async { + // It's a brand new picture, with crop parameters. + // For performance reasons, we do not crop the image full-size here, + // but in the background task. + // For privacy reasons, we won't send the full image to the server and + // let it crop it: we'll send the cropped image directly. + final File fullFile = await copyFullImageFile( + directory, + sequenceNumber, + inputFile, + ); + if (!context.mounted) { + return null; + } + return getCropParameters( + controller: controller, + fullFile: fullFile, + smallCroppedFile: smallCroppedFile, + offsets: offsets, + ); + } +} diff --git a/packages/smooth_app/lib/pages/scan/camera_scan_page.dart b/packages/smooth_app/lib/pages/scan/camera_scan_page.dart index f9442cae2cf..36c793cf0f5 100644 --- a/packages/smooth_app/lib/pages/scan/camera_scan_page.dart +++ b/packages/smooth_app/lib/pages/scan/camera_scan_page.dart @@ -19,10 +19,21 @@ class CameraScannerPage extends StatefulWidget { const CameraScannerPage(); @override - CameraScannerPageState createState() => CameraScannerPageState(); + State createState() => _CameraScannerPageState(); + + static Future onCameraFlashError(BuildContext context) async { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return showDialog( + context: context, + builder: (_) => SmoothAlertDialog( + title: appLocalizations.camera_flash_error_dialog_title, + body: Text(appLocalizations.camera_flash_error_dialog_message), + ), + ); + } } -class CameraScannerPageState extends State +class _CameraScannerPageState extends State with TraceableClientMixin { final GlobalKey> _headerKey = GlobalKey(); @@ -84,17 +95,21 @@ class CameraScannerPageState extends State return ScreenVisibilityDetector( child: Stack( children: [ - GlobalVars.barcodeScanner.getScanner( - onScan: _onNewBarcodeDetected, - hapticFeedback: () => SmoothHapticFeedback.click(), - onCameraFlashError: _onCameraFlashError, - trackCustomEvent: AnalyticsHelper.trackCustomEvent, - hasMoreThanOneCamera: CameraHelper.hasMoreThanOneCamera, - toggleCameraModeTooltip: appLocalizations.camera_toggle_camera, - toggleFlashModeTooltip: appLocalizations.camera_toggle_flash, - contentPadding: _model.compareFeatureEnabled - ? EdgeInsets.only(top: _headerHeight ?? 0.0) - : null, + Semantics( + label: appLocalizations.camera_window_accessibility_label, + explicitChildNodes: true, + child: GlobalVars.barcodeScanner.getScanner( + onScan: _onNewBarcodeDetected, + hapticFeedback: () => SmoothHapticFeedback.click(), + onCameraFlashError: CameraScannerPage.onCameraFlashError, + trackCustomEvent: AnalyticsHelper.trackCustomEvent, + hasMoreThanOneCamera: CameraHelper.hasMoreThanOneCamera, + toggleCameraModeTooltip: appLocalizations.camera_toggle_camera, + toggleFlashModeTooltip: appLocalizations.camera_toggle_flash, + contentPadding: _model.compareFeatureEnabled + ? EdgeInsets.only(top: _headerHeight ?? 0.0) + : null, + ), ), Align( alignment: Alignment.topCenter, @@ -115,16 +130,4 @@ class CameraScannerPageState extends State _userPreferences.incrementScanCount(); return true; } - - void _onCameraFlashError(BuildContext context) { - final AppLocalizations appLocalizations = AppLocalizations.of(context); - - showDialog( - context: context, - builder: (_) => SmoothAlertDialog( - title: appLocalizations.camera_flash_error_dialog_title, - body: Text(appLocalizations.camera_flash_error_dialog_message), - ), - ); - } } diff --git a/packages/smooth_app/lib/pages/scan/carousel/main_card/scan_main_card.dart b/packages/smooth_app/lib/pages/scan/carousel/main_card/scan_main_card.dart new file mode 100644 index 00000000000..26e15c4bf4d --- /dev/null +++ b/packages/smooth_app/lib/pages/scan/carousel/main_card/scan_main_card.dart @@ -0,0 +1,189 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/news_feed/newsfeed_provider.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/helpers/provider_helper.dart'; +import 'package:smooth_app/helpers/strings_helper.dart'; +import 'package:smooth_app/pages/navigator/app_navigator.dart'; +import 'package:smooth_app/pages/scan/carousel/main_card/scan_tagline.dart'; +import 'package:smooth_app/resources/app_icons.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; +import 'package:smooth_app/themes/theme_provider.dart'; + +class ScanMainCard extends StatelessWidget { + const ScanMainCard(); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Expanded( + child: ConsumerFilter( + buildWhen: + (AppNewsProvider? previousValue, AppNewsProvider currentValue) { + return previousValue?.hasContent != currentValue.hasContent; + }, + builder: (BuildContext context, AppNewsProvider newsFeed, _) { + if (!newsFeed.hasContent) { + return const _SearchCard( + expandedMode: true, + ); + } else { + return Semantics( + explicitChildNodes: true, + child: const Column( + children: [ + Expanded( + flex: 6, + child: _SearchCard( + expandedMode: false, + ), + ), + SizedBox(height: SMALL_SPACE), + Expanded( + flex: 4, + child: ScanTagLine(), + ), + ], + ), + ); + } + }, + ), + ), + ], + ); + } +} + +class _SearchCard extends StatelessWidget { + const _SearchCard({ + required this.expandedMode, + }); + + /// Expanded is when this card is the only one (no tagline, no app review…) + final bool expandedMode; + + @override + Widget build(BuildContext context) { + final AppLocalizations localizations = AppLocalizations.of(context); + final bool lightTheme = !context.watch().isDarkMode(context); + + final Widget widget = SmoothCard( + color: lightTheme ? Colors.grey.withOpacity(0.1) : Colors.black, + padding: const EdgeInsets.symmetric( + vertical: MEDIUM_SPACE, + horizontal: LARGE_SPACE, + ), + margin: const EdgeInsets.symmetric( + horizontal: 0.0, + vertical: VERY_SMALL_SPACE, + ), + ignoreDefaultSemantics: true, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SvgPicture.asset( + lightTheme + ? 'assets/app/logo_text_black.svg' + : 'assets/app/logo_text_white.svg', + semanticsLabel: localizations.homepage_main_card_logo_description, + ), + FormattedText( + text: localizations.homepage_main_card_subheading, + textAlign: TextAlign.center, + textStyle: const TextStyle(height: 1.3), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: SMALL_SPACE), + child: _SearchBar(), + ), + ], + ), + ); + + if (expandedMode) { + return ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.4, + ), + child: widget, + ); + } else { + return widget; + } + } +} + +class _SearchBar extends StatelessWidget { + const _SearchBar(); + + static const double SEARCH_BAR_HEIGHT = 47.0; + + @override + Widget build(BuildContext context) { + final AppLocalizations localizations = AppLocalizations.of(context); + final SmoothColorsThemeExtension theme = + Theme.of(context).extension()!; + final bool lightTheme = !context.watch().isDarkMode(context); + + return Semantics( + button: true, + child: SizedBox( + height: SEARCH_BAR_HEIGHT, + child: InkWell( + onTap: () => AppNavigator.of(context).push(AppRoutes.SEARCH), + borderRadius: BorderRadius.circular(30.0), + child: Ink( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + color: lightTheme ? Colors.white : theme.greyDark, + border: Border.all(color: theme.primaryBlack), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.only( + start: 20.0, + end: 10.0, + bottom: 3.0, + ), + child: Text( + localizations.homepage_main_card_search_field_hint, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: lightTheme ? Colors.black : Colors.white, + ), + ), + ), + ), + AspectRatio( + aspectRatio: 1.0, + child: DecoratedBox( + decoration: BoxDecoration( + color: theme.primaryDark, + shape: BoxShape.circle, + ), + child: const Padding( + padding: EdgeInsets.all(10.0), + child: Search( + size: 20.0, + color: Colors.white, + ), + ), + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/scan/carousel/main_card/scan_tagline.dart b/packages/smooth_app/lib/pages/scan/carousel/main_card/scan_tagline.dart new file mode 100644 index 00000000000..8404fe114e1 --- /dev/null +++ b/packages/smooth_app/lib/pages/scan/carousel/main_card/scan_tagline.dart @@ -0,0 +1,465 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:shimmer/shimmer.dart'; +import 'package:smooth_app/cards/category_cards/svg_cache.dart'; +import 'package:smooth_app/data_models/news_feed/newsfeed_model.dart'; +import 'package:smooth_app/data_models/news_feed/newsfeed_provider.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/widgets/smooth_card.dart'; +import 'package:smooth_app/helpers/launch_url_helper.dart'; +import 'package:smooth_app/helpers/provider_helper.dart'; +import 'package:smooth_app/helpers/strings_helper.dart'; +import 'package:smooth_app/resources/app_icons.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; +import 'package:smooth_app/themes/theme_provider.dart'; + +class ScanTagLine extends StatelessWidget { + const ScanTagLine({super.key}); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider<_ScanNewsFeedProvider>( + create: (BuildContext context) => _ScanNewsFeedProvider(context), + child: Consumer<_ScanNewsFeedProvider>( + builder: ( + BuildContext context, + _ScanNewsFeedProvider scanTagLineProvider, + Widget? child, + ) { + final _ScanTagLineState state = scanTagLineProvider.value; + + return switch (state) { + _ScanTagLineStateLoading() => const _ScanTagLineLoading(), + _ScanTagLineStateNoContent() => EMPTY_WIDGET, + _ScanTagLineStateLoaded() => _ScanTagLineContent( + news: state.tagLine, + ), + }; + }, + ), + ); + } +} + +class _ScanTagLineLoading extends StatelessWidget { + const _ScanTagLineLoading(); + + @override + Widget build(BuildContext context) { + return Shimmer.fromColors( + baseColor: Theme.of(context) + .extension()! + .primaryMedium, + highlightColor: Colors.white, + child: const SmoothCard( + child: SizedBox( + width: double.infinity, + height: 200.0, + ), + ), + ); + } +} + +class _ScanTagLineContent extends StatefulWidget { + const _ScanTagLineContent({ + required this.news, + }); + + final Iterable news; + + @override + State<_ScanTagLineContent> createState() => _ScanTagLineContentState(); +} + +class _ScanTagLineContentState extends State<_ScanTagLineContent> { + Timer? _timer; + int _index = -1; + + @override + void initState() { + super.initState(); + _rotateNews(); + } + + void _rotateNews() { + _timer?.cancel(); + + _index++; + if (_index >= widget.news.length) { + _index = 0; + } + + _timer = Timer(const Duration(minutes: 30), () => _rotateNews()); + } + + @override + Widget build(BuildContext context) { + final ThemeProvider themeProvider = context.watch(); + final SmoothColorsThemeExtension theme = + Theme.of(context).extension()!; + final AppNewsItem currentNews = widget.news.elementAt(_index); + + // Default values seem weird + const Radius radius = Radius.circular(16.0); + + return Column( + children: [ + DecoratedBox( + decoration: BoxDecoration( + color: currentNews.style?.titleBackground ?? + (!themeProvider.isDarkMode(context) + ? theme.primarySemiDark + : theme.primaryBlack), + borderRadius: const BorderRadiusDirectional.only( + topStart: radius, + topEnd: radius, + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: VERY_SMALL_SPACE, + horizontal: MEDIUM_SPACE, + ), + child: _TagLineContentTitle( + title: currentNews.title, + backgroundColor: currentNews.style?.titleBackground, + indicatorColor: currentNews.style?.titleIndicatorColor, + titleColor: currentNews.style?.titleTextColor, + ), + ), + ), + Expanded( + child: DecoratedBox( + decoration: BoxDecoration( + color: currentNews.style?.contentBackgroundColor ?? + (!themeProvider.isDarkMode(context) + ? theme.primaryMedium + : theme.primaryDark), + borderRadius: const BorderRadiusDirectional.only( + bottomStart: radius, + bottomEnd: radius, + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: SMALL_SPACE, + horizontal: MEDIUM_SPACE, + ), + child: Column( + children: [ + Expanded( + child: _TagLineContentBody( + message: currentNews.message, + textColor: currentNews.style?.messageTextColor, + image: currentNews.image, + ), + ), + const SizedBox(height: SMALL_SPACE), + Align( + alignment: AlignmentDirectional.bottomEnd, + child: _TagLineContentButton( + link: currentNews.url, + label: currentNews.buttonLabel, + backgroundColor: currentNews.style?.buttonBackground, + foregroundColor: currentNews.style?.buttonTextColor, + ), + ), + ], + ), + ), + ), + ), + ], + ); + } + + @override + void dispose() { + _timer?.cancel(); + super.dispose(); + } +} + +class _TagLineContentTitle extends StatelessWidget { + const _TagLineContentTitle({ + required this.title, + this.backgroundColor, + this.indicatorColor, + this.titleColor, + }); + + final String title; + final Color? backgroundColor; + final Color? indicatorColor; + final Color? titleColor; + + @override + Widget build(BuildContext context) { + final SmoothColorsThemeExtension theme = + Theme.of(context).extension()!; + final AppLocalizations localizations = AppLocalizations.of(context); + + return Semantics( + label: localizations.scan_tagline_news_item_accessibility(title), + excludeSemantics: true, + child: ConstrainedBox( + constraints: const BoxConstraints(minHeight: 30.0), + child: Row( + children: [ + SizedBox.square( + dimension: 11.0, + child: DecoratedBox( + decoration: BoxDecoration( + color: indicatorColor ?? theme.secondaryLight, + borderRadius: const BorderRadius.all(ROUNDED_RADIUS), + ), + ), + ), + const SizedBox(width: VERY_SMALL_SPACE), + Expanded( + child: Text( + title, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + color: titleColor ?? Colors.white, + ), + )) + ], + ), + ), + ); + } +} + +class _TagLineContentBody extends StatefulWidget { + const _TagLineContentBody({ + required this.message, + this.textColor, + this.image, + }); + + final String message; + final Color? textColor; + final AppNewsImage? image; + + @override + State<_TagLineContentBody> createState() => _TagLineContentBodyState(); +} + +class _TagLineContentBodyState extends State<_TagLineContentBody> { + bool _imageError = false; + + @override + Widget build(BuildContext context) { + final ThemeProvider themeProvider = context.watch(); + final SmoothColorsThemeExtension theme = + Theme.of(context).extension()!; + + final Widget text = FormattedText( + text: widget.message, + textStyle: TextStyle( + color: widget.textColor ?? + (!themeProvider.isDarkMode(context) + ? theme.primarySemiDark + : theme.primaryLight), + ), + ); + + if (widget.image == null) { + return text; + } + + final int imageFlex = ((widget.image!.width ?? 0.2) * 10).toInt(); + return Row( + children: [ + if (!_imageError) ...[ + Expanded( + flex: imageFlex, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.06, + ), + child: AspectRatio( + aspectRatio: 1.0, + child: _image(), + ), + ), + ), + const SizedBox(width: MEDIUM_SPACE), + ], + Expanded( + flex: 10 - imageFlex, + child: text, + ), + ], + ); + } + + Widget _image() { + if (widget.image!.src?.endsWith('svg') == true) { + return SvgCache( + widget.image!.src, + semanticsLabel: widget.image!.alt, + ); + } else { + return Image.network( + semanticLabel: widget.image!.alt, + errorBuilder: ( + BuildContext context, + Object error, + StackTrace? stackTrace, + ) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_imageError != true) { + setState(() => _imageError = true); + } + }); + + return EMPTY_WIDGET; + }, + widget.image!.src ?? '-', + ); + } + } +} + +class _TagLineContentButton extends StatelessWidget { + const _TagLineContentButton({ + required this.link, + this.label, + this.backgroundColor, + this.foregroundColor, + }); + + final String link; + final String? label; + final Color? backgroundColor; + final Color? foregroundColor; + + @override + Widget build(BuildContext context) { + final AppLocalizations localizations = AppLocalizations.of(context); + final SmoothColorsThemeExtension theme = + Theme.of(context).extension()!; + + return FilledButton( + style: FilledButton.styleFrom( + backgroundColor: backgroundColor ?? theme.primaryBlack, + foregroundColor: foregroundColor ?? Colors.white, + padding: const EdgeInsets.symmetric( + vertical: VERY_SMALL_SPACE, + horizontal: MEDIUM_SPACE, + ), + minimumSize: const Size(0, 20.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(label ?? localizations.tagline_feed_news_button), + const SizedBox(width: MEDIUM_SPACE), + const Arrow.right( + size: 12.0, + ), + ], + ), + onPressed: () => LaunchUrlHelper.launchURLAndFollowDeepLinks( + context, + link, + ), + ); + } +} + +/// Listen to [AppNewsProvider] feed and provide a list of [AppNewsItem] +/// randomly sorted by unread, then displayed and clicked news. +class _ScanNewsFeedProvider extends ValueNotifier<_ScanTagLineState> { + _ScanNewsFeedProvider(BuildContext context) + : _newsFeedProvider = context.read(), + _userPreferences = context.read(), + super(const _ScanTagLineStateLoading()) { + _newsFeedProvider.addListener(_onNewsFeedStateChanged); + // Refresh with the current state + _onNewsFeedStateChanged(); + } + + final AppNewsProvider _newsFeedProvider; + final UserPreferences _userPreferences; + + void _onNewsFeedStateChanged() { + switch (_newsFeedProvider.state) { + case AppNewsStateLoading(): + emit(const _ScanTagLineStateLoading()); + case AppNewsStateError(): + emit(const _ScanTagLineStateNoContent()); + case AppNewsStateLoaded(): + _onTagLineContentAvailable( + (_newsFeedProvider.state as AppNewsStateLoaded).content); + } + } + + Future _onTagLineContentAvailable(AppNews tagLine) async { + if (!tagLine.feed.isNotEmpty) { + emit(const _ScanTagLineStateNoContent()); + return; + } + + final List unreadNews = []; + final List displayedNews = []; + final List clickedNews = []; + + final List taglineFeedAlreadyClickedNews = + _userPreferences.taglineFeedClickedNews; + final List taglineFeedAlreadyDisplayedNews = + _userPreferences.taglineFeedDisplayedNews; + + for (final AppNewsFeedItem feedItem in tagLine.feed.news) { + if (taglineFeedAlreadyClickedNews.contains(feedItem.id)) { + clickedNews.add(feedItem.news); + } else if (taglineFeedAlreadyDisplayedNews.contains(feedItem.id)) { + displayedNews.add(feedItem.news); + } else { + unreadNews.add(feedItem.news); + } + } + + emit( + _ScanTagLineStateLoaded( + [ + ...unreadNews..shuffle(), + ...displayedNews..shuffle(), + ...clickedNews..shuffle(), + ], + ), + ); + } + + @override + void dispose() { + _newsFeedProvider.removeListener(_onNewsFeedStateChanged); + super.dispose(); + } +} + +sealed class _ScanTagLineState { + const _ScanTagLineState(); +} + +class _ScanTagLineStateLoading extends _ScanTagLineState { + const _ScanTagLineStateLoading(); +} + +class _ScanTagLineStateNoContent extends _ScanTagLineState { + const _ScanTagLineStateNoContent(); +} + +class _ScanTagLineStateLoaded extends _ScanTagLineState { + const _ScanTagLineStateLoaded(this.tagLine); + + final Iterable tagLine; +} diff --git a/packages/smooth_app/lib/pages/scan/carousel/scan_carousel.dart b/packages/smooth_app/lib/pages/scan/carousel/scan_carousel.dart new file mode 100644 index 00000000000..6ab2e199366 --- /dev/null +++ b/packages/smooth_app/lib/pages/scan/carousel/scan_carousel.dart @@ -0,0 +1,195 @@ +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:scanner_shared/scanner_shared.dart' hide EMPTY_WIDGET; +import 'package:smooth_app/cards/product_cards/smooth_product_card_error.dart'; +import 'package:smooth_app/cards/product_cards/smooth_product_card_loading.dart'; +import 'package:smooth_app/cards/product_cards/smooth_product_card_not_found.dart'; +import 'package:smooth_app/cards/product_cards/smooth_product_card_thanks.dart'; +import 'package:smooth_app/data_models/continuous_scan_model.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/scan/carousel/main_card/scan_main_card.dart'; +import 'package:smooth_app/pages/scan/carousel/scan_carousel_manager.dart'; +import 'package:smooth_app/pages/scan/scan_product_card_loader.dart'; + +class ScanPageCarousel extends StatefulWidget { + const ScanPageCarousel({ + this.onPageChangedTo, + }); + + final Function(int page, String? productBarcode)? onPageChangedTo; + + @override + State createState() => _ScanPageCarouselState(); +} + +class _ScanPageCarouselState extends State { + static const double HORIZONTAL_SPACE_BETWEEN_CARDS = 5.0; + + List barcodes = []; + String? _lastConsultedBarcode; + int? _carrouselMovingTo; + int _lastIndex = 0; + + late ContinuousScanModel _model; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _model = context.watch(); + + if (!ExternalScanCarouselManager.read(context).controller.ready) { + return; + } + + barcodes = _model.getBarcodes(); + + if (barcodes.isEmpty) { + // Ensure to reset all variables + _lastConsultedBarcode = null; + _carrouselMovingTo = null; + _lastIndex = 0; + return; + } else if (_lastConsultedBarcode == _model.latestConsultedBarcode) { + // Prevent multiple irrelevant movements + return; + } + + _lastConsultedBarcode = _model.latestConsultedBarcode; + final int cardsCount = barcodes.length + 1; + + if (_model.latestConsultedBarcode != null && + _model.latestConsultedBarcode!.isNotEmpty) { + final int indexBarcode = barcodes.indexOf(_model.latestConsultedBarcode!); + if (indexBarcode >= 0) { + final int indexCarousel = indexBarcode + 1; + _moveControllerTo(indexCarousel); + } else { + if (_lastIndex > cardsCount) { + _moveControllerTo(cardsCount); + } else { + _moveControllerTo(_lastIndex); + } + } + } else { + _moveControllerTo(0); + } + } + + Future _moveControllerTo(int page) async { + if (_carrouselMovingTo == null && _lastIndex != page) { + widget.onPageChangedTo?.call( + page, + page >= 1 ? barcodes[page - 1] : null, + ); + + _carrouselMovingTo = page; + ExternalScanCarouselManager.read(context).animatePageTo(page); + _carrouselMovingTo = null; + } + } + + @override + Widget build(BuildContext context) { + barcodes = _model.getBarcodes(); + + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return CarouselSlider.builder( + itemCount: barcodes.length + 1, + itemBuilder: + (BuildContext context, int itemIndex, int itemRealIndex) { + return SizedBox.expand( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: HORIZONTAL_SPACE_BETWEEN_CARDS, + ), + child: itemIndex == 0 + ? const ScanMainCard() + : _getWidget(itemIndex - 1), + ), + ); + }, + carouselController: + ExternalScanCarouselManager.watch(context).controller, + options: CarouselOptions( + enlargeCenterPage: false, + viewportFraction: _computeViewPortFraction(), + height: constraints.maxHeight, + enableInfiniteScroll: false, + onPageChanged: (int index, CarouselPageChangedReason reason) { + _lastIndex = index; + + if (index > 0) { + if (reason == CarouselPageChangedReason.manual) { + _model.lastConsultedBarcode = barcodes[index - 1]; + _lastConsultedBarcode = _model.latestConsultedBarcode; + } + } else if (index == 0) { + _model.lastConsultedBarcode = null; + _lastConsultedBarcode = null; + } + }, + ), + ); + }, + ); + } + + /// Displays the card for this [index] of a list of [barcodes] + /// + /// There are special cases when the item display is refreshed + /// after the product disappeared and before the whole carousel is refreshed. + /// In those cases, we don't want the app to crash and display a Container + /// instead in the meanwhile. + Widget _getWidget(final int index) { + if (index >= barcodes.length) { + return EMPTY_WIDGET; + } + final String barcode = barcodes[index]; + switch (_model.getBarcodeState(barcode)!) { + case ScannedProductState.FOUND: + case ScannedProductState.CACHED: + return ScanProductCardLoader(barcode); + case ScannedProductState.LOADING: + return SmoothProductCardLoading( + barcode: barcode, + onRemoveProduct: (_) => _model.removeBarcode(barcode), + ); + case ScannedProductState.NOT_FOUND: + return SmoothProductCardNotFound( + barcode: barcode, + onAddProduct: () async { + await _model.refresh(); + setState(() {}); + }, + onRemoveProduct: (_) => _model.removeBarcode(barcode), + ); + case ScannedProductState.THANKS: + return const SmoothProductCardThanks(); + case ScannedProductState.ERROR_INTERNET: + return SmoothProductCardError( + barcode: barcode, + errorType: ScannedProductState.ERROR_INTERNET, + ); + case ScannedProductState.ERROR_INVALID_CODE: + return SmoothProductCardError( + barcode: barcode, + errorType: ScannedProductState.ERROR_INVALID_CODE, + ); + } + } + + double _computeViewPortFraction() { + final double screenWidth = MediaQuery.sizeOf(context).width; + if (barcodes.isEmpty) { + return 0.95; + } + + return (screenWidth - + (SmoothBarcodeScannerVisor.CORNER_PADDING * 2) - + (SmoothBarcodeScannerVisor.STROKE_WIDTH * 2) + + (HORIZONTAL_SPACE_BETWEEN_CARDS * 4)) / + screenWidth; + } +} diff --git a/packages/smooth_app/lib/pages/carousel_manager.dart b/packages/smooth_app/lib/pages/scan/carousel/scan_carousel_manager.dart similarity index 75% rename from packages/smooth_app/lib/pages/carousel_manager.dart rename to packages/smooth_app/lib/pages/scan/carousel/scan_carousel_manager.dart index d3ad42eadf4..02bccf79825 100644 --- a/packages/smooth_app/lib/pages/carousel_manager.dart +++ b/packages/smooth_app/lib/pages/scan/carousel/scan_carousel_manager.dart @@ -2,36 +2,38 @@ import 'package:carousel_slider/carousel_controller.dart'; import 'package:flutter/material.dart'; import 'package:smooth_app/helpers/haptic_feedback_helper.dart'; -class ExternalCarouselManager extends StatefulWidget { - const ExternalCarouselManager({ +/// Allow to control the [ScanPageCarousel] from outside +class ExternalScanCarouselManager extends StatefulWidget { + const ExternalScanCarouselManager({ super.key, required this.child, }); final Widget child; - static ExternalCarouselManagerState watch(BuildContext context) { + static ExternalScanCarouselManagerState watch(BuildContext context) { return context .dependOnInheritedWidgetOfExactType<_InheritedCarouselManager>()! .state; } - static ExternalCarouselManagerState? find(BuildContext context) { + static ExternalScanCarouselManagerState? find(BuildContext context) { return context .findAncestorWidgetOfExactType<_InheritedCarouselManager>() ?.state; } - static ExternalCarouselManagerState read(BuildContext context) { + static ExternalScanCarouselManagerState read(BuildContext context) { return find(context)!; } @override - State createState() => - ExternalCarouselManagerState(); + State createState() => + ExternalScanCarouselManagerState(); } -class ExternalCarouselManagerState extends State { +class ExternalScanCarouselManagerState + extends State { final CarouselController _controller = CarouselController(); /// A hidden attribute to force to return to the Scanner tab @@ -75,7 +77,7 @@ class ExternalCarouselManagerState extends State { CarouselController get controller => _controller; - bool updateShouldNotify(ExternalCarouselManagerState oldState) { + bool updateShouldNotify(ExternalScanCarouselManagerState oldState) { return oldState.currentBarcode != currentBarcode || _forceShowScannerTab; } } @@ -87,7 +89,7 @@ class _InheritedCarouselManager extends InheritedWidget { Key? key, }) : super(key: key, child: child); - final ExternalCarouselManagerState state; + final ExternalScanCarouselManagerState state; @override bool updateShouldNotify(_InheritedCarouselManager oldWidget) { diff --git a/packages/smooth_app/lib/pages/scan/scan_header.dart b/packages/smooth_app/lib/pages/scan/scan_header.dart index ab419653504..53cbe5bbb6e 100644 --- a/packages/smooth_app/lib/pages/scan/scan_header.dart +++ b/packages/smooth_app/lib/pages/scan/scan_header.dart @@ -24,7 +24,7 @@ class _ScanHeaderState extends State { final ContinuousScanModel model = context.watch(); final ButtonStyle buttonStyle = ButtonStyle( - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(18.0)), ), diff --git a/packages/smooth_app/lib/pages/scan/scan_page.dart b/packages/smooth_app/lib/pages/scan/scan_page.dart index 500972deb9c..26f22f14e30 100644 --- a/packages/smooth_app/lib/pages/scan/scan_page.dart +++ b/packages/smooth_app/lib/pages/scan/scan_page.dart @@ -15,7 +15,7 @@ import 'package:smooth_app/helpers/camera_helper.dart'; import 'package:smooth_app/helpers/haptic_feedback_helper.dart'; import 'package:smooth_app/helpers/permission_helper.dart'; import 'package:smooth_app/pages/scan/camera_scan_page.dart'; -import 'package:smooth_app/widgets/smooth_product_carousel.dart'; +import 'package:smooth_app/pages/scan/carousel/scan_carousel.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; class ScanPage extends StatefulWidget { @@ -64,7 +64,7 @@ class _ScanPageState extends State { color: Colors.white, child: SafeArea( child: Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ if (hasACamera) @@ -92,8 +92,7 @@ class _ScanPageState extends State { flex: _carouselHeightPct, child: Padding( padding: const EdgeInsetsDirectional.only(bottom: 10.0), - child: SmoothProductCarousel( - containSearchCard: true, + child: ScanPageCarousel( onPageChangedTo: (int page, String? barcode) async { if (barcode == null) { // We only notify for new products diff --git a/packages/smooth_app/lib/pages/scan/search_history_view.dart b/packages/smooth_app/lib/pages/scan/search_history_view.dart index 1db74f9929c..a4b0a94507a 100644 --- a/packages/smooth_app/lib/pages/scan/search_history_view.dart +++ b/packages/smooth_app/lib/pages/scan/search_history_view.dart @@ -2,18 +2,23 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; -import 'package:smooth_app/database/dao_string_list.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/pages/product/common/search_helper.dart'; +import 'package:smooth_app/pages/product/common/search_preloaded_item.dart'; class SearchHistoryView extends StatefulWidget { const SearchHistoryView({ - this.onTap, - this.focusNode, + required this.onTap, + required this.focusNode, + required this.searchHelper, + required this.preloadedList, }); - final void Function(String)? onTap; - final FocusNode? focusNode; + final void Function(String) onTap; + final FocusNode focusNode; + final SearchHelper searchHelper; + final List preloadedList; @override State createState() => _SearchHistoryViewState(); @@ -31,8 +36,8 @@ class _SearchHistoryViewState extends State { void _fetchQueries() { final LocalDatabase localDatabase = context.watch(); final List queries = - DaoStringList(localDatabase).getAll(DaoStringList.keySearchHistory); - setState(() => _queries = queries.reversed.toList()); + widget.searchHelper.getAllQueries(localDatabase); + setState(() => _queries = queries); } @override @@ -41,35 +46,52 @@ class _SearchHistoryViewState extends State { data: ListTileThemeData( titleTextStyle: const TextStyle(fontSize: 20.0), minLeadingWidth: 18.0, - iconColor: Theme.of(context).colorScheme.onBackground, - textColor: Theme.of(context).colorScheme.onBackground, + iconColor: Theme.of(context).colorScheme.onSurface, + textColor: Theme.of(context).colorScheme.onSurface, ), child: ListView.builder( itemBuilder: (BuildContext context, int i) { if (i == 0) { return _SearchItemPasteFromClipboard( - onData: (String data) => widget.onTap?.call(data), + onData: (String data) => widget.onTap.call(data), ); } + i--; + + if (i < widget.preloadedList.length) { + final SearchPreloadedItem item = widget.preloadedList[i]; + return item.getWidget( + context, + onDismissItem: () async { + // we need an immediate action for the display refresh + widget.preloadedList.removeAt(i); + // and we need to impact the database too + await item.delete(context); + + setState(() {}); + }, + ); + } + i -= widget.preloadedList.length; - final String query = _queries[i - 1]; - + final String query = _queries[i]; return _SearchHistoryTile( query: query, - onTap: () => widget.onTap?.call(query), + onTap: () => widget.onTap.call(query), onEditItem: () => _onEditItem(query), onDismissItem: () async { // we need an immediate action for the display refresh _queries.remove(query); // and we need to impact the database too final LocalDatabase localDatabase = context.read(); - await DaoStringList(localDatabase) - .remove(DaoStringList.keySearchHistory, query); + await widget.searchHelper.removeQuery(localDatabase, query); setState(() {}); }, ); }, - itemCount: _queries.length + 1, // +1 for the "Copy to clipboard" + itemCount: _queries.length + + widget.preloadedList.length + + 1, // +1 for the "Copy from clipboard" ), ); } @@ -86,7 +108,7 @@ class _SearchHistoryViewState extends State { // If the keyboard is hidden, show it. if (View.of(context).viewInsets.bottom == 0) { - widget.focusNode?.unfocus(); + widget.focusNode.unfocus(); WidgetsBinding.instance.addPostFrameCallback((_) { FocusScope.of(context).requestFocus(widget.focusNode); diff --git a/packages/smooth_app/lib/pages/scan/search_page.dart b/packages/smooth_app/lib/pages/scan/search_page.dart index 2a9d293cfe0..f20a0ef87da 100644 --- a/packages/smooth_app/lib/pages/scan/search_page.dart +++ b/packages/smooth_app/lib/pages/scan/search_page.dart @@ -1,107 +1,25 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; -import 'package:smooth_app/data_models/fetched_product.dart'; -import 'package:smooth_app/database/dao_string_list.dart'; -import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/duration_constants.dart'; -import 'package:smooth_app/helpers/analytics_helper.dart'; -import 'package:smooth_app/helpers/string_extension.dart'; -import 'package:smooth_app/pages/navigator/app_navigator.dart'; -import 'package:smooth_app/pages/product/common/product_dialog_helper.dart'; -import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; +import 'package:smooth_app/pages/product/common/search_helper.dart'; +import 'package:smooth_app/pages/product/common/search_preloaded_item.dart'; import 'package:smooth_app/pages/scan/search_history_view.dart'; -import 'package:smooth_app/query/keywords_product_query.dart'; import 'package:smooth_app/widgets/smooth_app_bar.dart'; import 'package:smooth_app/widgets/smooth_scaffold.dart'; -void _performSearch( - BuildContext context, - String query, { - EditProductQueryCallback? editProductQueryCallback, -}) { - query = query.trim(); - if (query.isEmpty) { - return; - } else if (query.removeSpaces().hasOnlyDigits()) { - // This maybe a barcode => remove spaces within the string - query = query.removeSpaces(); - } - - final LocalDatabase localDatabase = context.read(); - DaoStringList(localDatabase).add(DaoStringList.keySearchHistory, query); - - if (int.tryParse(query) != null) { - _onSubmittedBarcode( - query, - context, - localDatabase, - ); - } else { - _onSubmittedText( - query, - context, - localDatabase, - editProductQueryCallback: editProductQueryCallback, - ); - } -} - -// used to be in now defunct `ChoosePage` -Future _onSubmittedBarcode( - final String value, - final BuildContext context, - final LocalDatabase localDatabase, -) async { - final ProductDialogHelper productDialogHelper = ProductDialogHelper( - barcode: value, - context: context, - localDatabase: localDatabase, - ); - final FetchedProduct fetchedProduct = - await productDialogHelper.openBestChoice(); - if (fetchedProduct.status == FetchedProductStatus.ok) { - AnalyticsHelper.trackSearch( - search: value, - searchCategory: 'barcode', - searchCount: 1, - ); - if (context.mounted) { - AppNavigator.of(context).push( - AppRoutes.PRODUCT( - fetchedProduct.product!.barcode!, - heroTag: 'search_${fetchedProduct.product!.barcode!}', - ), - extra: fetchedProduct.product, - ); - } - } else { - AnalyticsHelper.trackSearch( - search: value, - searchCategory: 'barcode', - searchCount: 0, - ); - productDialogHelper.openError(fetchedProduct); - } -} +class SearchPage extends StatefulWidget { + const SearchPage( + this.searchHelper, { + this.preloadedList, + this.autofocus = true, + }); -// used to be in now defunct `ChoosePage` -Future _onSubmittedText( - final String value, - final BuildContext context, - final LocalDatabase localDatabase, { - EditProductQueryCallback? editProductQueryCallback, -}) async => - ProductQueryPageHelper().openBestChoice( - name: value, - localDatabase: localDatabase, - productQuery: KeywordsProductQuery(value), - context: context, - editQueryCallback: editProductQueryCallback, - ); + final SearchHelper searchHelper; + final List? preloadedList; + final bool autofocus; -class SearchPage extends StatefulWidget { @override State createState() => _SearchPageState(); } @@ -122,21 +40,23 @@ class _SearchPageState extends State { Padding( padding: const EdgeInsets.all(10.0), child: SearchField( - autofocus: true, + autofocus: widget.autofocus, focusNode: _searchFocusNode, + searchHelper: widget.searchHelper, ), ), Expanded( child: SearchHistoryView( focusNode: _searchFocusNode, - onTap: (String query) => _performSearch( + onTap: (String query) => + widget.searchHelper.searchWithController( context, query, - editProductQueryCallback: (String productName) { - _searchTextController.text = productName; - _searchFocusNode.requestFocus(); - }, + _searchTextController, + _searchFocusNode, ), + searchHelper: widget.searchHelper, + preloadedList: widget.preloadedList ?? [], ), ), ], @@ -148,6 +68,7 @@ class _SearchPageState extends State { class SearchField extends StatefulWidget { const SearchField({ + required this.searchHelper, this.autofocus = false, this.showClearButton = true, this.readOnly = false, @@ -157,6 +78,7 @@ class SearchField extends StatefulWidget { this.focusNode, }); + final SearchHelper searchHelper; final bool autofocus; final bool showClearButton; @@ -235,7 +157,7 @@ class _SearchFieldState extends State { horizontal: 25.0, vertical: 17.0, ), - hintText: localizations.search, + hintText: widget.searchHelper.getHintText(localizations), suffixIcon: widget.showClearButton ? _buildClearButton(localizations) : null, ); @@ -274,13 +196,11 @@ class _SearchFieldState extends State { textInputAction: TextInputAction.search, controller: _controller, focusNode: _focusNode, - onSubmitted: (String query) => _performSearch( + onSubmitted: (String query) => widget.searchHelper.searchWithController( context, query, - editProductQueryCallback: (String productName) { - _controller.text = productName; - _focusNode.requestFocus(); - }, + _controller, + _focusNode, ), decoration: inputDecoration, style: textStyle, @@ -336,6 +256,7 @@ class _SearchFieldState extends State { } } + // FIXME(monsieurtanuki): when we paste from the clipboard and then clear, _isEmpty is not changed and therefore we pop instead of clearing. void _handleClear() { if (_isEmpty) { Navigator.pop(context); diff --git a/packages/smooth_app/lib/pages/scan/search_product_helper.dart b/packages/smooth_app/lib/pages/scan/search_product_helper.dart new file mode 100644 index 00000000000..94fab981bbe --- /dev/null +++ b/packages/smooth_app/lib/pages/scan/search_product_helper.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/fetched_product.dart'; +import 'package:smooth_app/database/dao_string_list.dart'; +import 'package:smooth_app/database/local_database.dart'; +import 'package:smooth_app/helpers/analytics_helper.dart'; +import 'package:smooth_app/helpers/string_extension.dart'; +import 'package:smooth_app/pages/navigator/app_navigator.dart'; +import 'package:smooth_app/pages/product/common/product_dialog_helper.dart'; +import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; +import 'package:smooth_app/pages/product/common/search_helper.dart'; +import 'package:smooth_app/query/keywords_product_query.dart'; + +/// Search helper dedicated to product search. +class SearchProductHelper extends SearchHelper { + const SearchProductHelper(); + + @override + String get historyKey => DaoStringList.keySearchProductHistory; + + @override + String getHintText(final AppLocalizations appLocalizations) => + appLocalizations.search; + + @override + void search( + BuildContext context, + String query, { + required SearchQueryCallback searchQueryCallback, + }) { + query = query.trim(); + if (query.isEmpty) { + return; + } else if (query.removeSpaces().hasOnlyDigits()) { + // This maybe a barcode => remove spaces within the string + query = query.removeSpaces(); + } + + final LocalDatabase localDatabase = context.read(); + addQuery(localDatabase, query); + + if (int.tryParse(query) != null) { + _onSubmittedBarcode( + query, + context, + localDatabase, + // TODO(monsieurtanuki): we should use searchQueryCallback here too, shouldn't we? + ); + } else { + _onSubmittedText( + query, + context, + localDatabase, + editProductQueryCallback: searchQueryCallback, + ); + } + } + +// used to be in now defunct `ChoosePage` + Future _onSubmittedBarcode( + final String value, + final BuildContext context, + final LocalDatabase localDatabase, + ) async { + final ProductDialogHelper productDialogHelper = ProductDialogHelper( + barcode: value, + context: context, + localDatabase: localDatabase, + ); + final FetchedProduct fetchedProduct = + await productDialogHelper.openBestChoice(); + if (fetchedProduct.status == FetchedProductStatus.ok) { + AnalyticsHelper.trackSearch( + search: value, + searchCategory: 'barcode', + searchCount: 1, + ); + if (context.mounted) { + AppNavigator.of(context).push( + AppRoutes.PRODUCT( + fetchedProduct.product!.barcode!, + heroTag: 'search_${fetchedProduct.product!.barcode!}', + ), + extra: fetchedProduct.product, + ); + } + } else { + AnalyticsHelper.trackSearch( + search: value, + searchCategory: 'barcode', + searchCount: 0, + ); + productDialogHelper.openError(fetchedProduct); + } + } + +// used to be in now defunct `ChoosePage` + Future _onSubmittedText( + final String value, + final BuildContext context, + final LocalDatabase localDatabase, { + SearchQueryCallback? editProductQueryCallback, + }) async => + ProductQueryPageHelper().openBestChoice( + name: value, + localDatabase: localDatabase, + productQuery: KeywordsProductQuery(value), + context: context, + editQueryCallback: editProductQueryCallback, + ); +} diff --git a/packages/smooth_app/lib/pages/user_management/forgot_password_page.dart b/packages/smooth_app/lib/pages/user_management/forgot_password_page.dart index 39934741925..42108e73d1e 100644 --- a/packages/smooth_app/lib/pages/user_management/forgot_password_page.dart +++ b/packages/smooth_app/lib/pages/user_management/forgot_password_page.dart @@ -69,7 +69,7 @@ class _ForgotPasswordPageState extends State Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); - final Size size = MediaQuery.of(context).size; + final Size size = MediaQuery.sizeOf(context); return SmoothScaffold( appBar: SmoothAppBar( @@ -150,11 +150,10 @@ class _ForgotPasswordPageState extends State } }, style: ButtonStyle( - minimumSize: MaterialStateProperty.all( + minimumSize: WidgetStateProperty.all( Size(size.width * 0.5, theme.buttonTheme.height + 10), ), - shape: - MaterialStateProperty.all( + shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: CIRCULAR_BORDER_RADIUS, ), diff --git a/packages/smooth_app/lib/pages/user_management/login_page.dart b/packages/smooth_app/lib/pages/user_management/login_page.dart index 9dbb268e8e5..cbda47ad48b 100644 --- a/packages/smooth_app/lib/pages/user_management/login_page.dart +++ b/packages/smooth_app/lib/pages/user_management/login_page.dart @@ -87,7 +87,7 @@ class _LoginPageState extends State with TraceableClientMixin { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); - final Size size = MediaQuery.of(context).size; + final Size size = MediaQuery.sizeOf(context); return SmoothScaffold( statusBarBackgroundColor: SmoothScaffold.semiTranslucentStatusBar, @@ -121,7 +121,7 @@ class _LoginPageState extends State with TraceableClientMixin { children: [ SvgPicture.asset( 'assets/preferences/login.svg', - height: MediaQuery.of(context).size.height * .15, + height: MediaQuery.sizeOf(context).height * .15, package: AppHelper.APP_PACKAGE, ), Text( @@ -221,12 +221,12 @@ class _LoginPageState extends State with TraceableClientMixin { ElevatedButton( onPressed: () => _login(context), style: ButtonStyle( - minimumSize: MaterialStateProperty.all( + minimumSize: WidgetStateProperty.all( Size(size.width * 0.5, theme.buttonTheme.height + 10), ), - shape: MaterialStateProperty.all< - RoundedRectangleBorder>( + shape: + WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: CIRCULAR_BORDER_RADIUS, ), @@ -249,14 +249,14 @@ class _LoginPageState extends State with TraceableClientMixin { //Forgot password TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( const EdgeInsets.symmetric( vertical: 10.0, horizontal: VERY_LARGE_SPACE, ), ), shape: - MaterialStateProperty.all( + WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: CIRCULAR_BORDER_RADIUS, ), @@ -304,15 +304,15 @@ class _LoginPageState extends State with TraceableClientMixin { } }, style: ButtonStyle( - side: MaterialStateProperty.all( + side: WidgetStateProperty.all( BorderSide( color: theme.colorScheme.primary, width: 2.0), ), - minimumSize: MaterialStateProperty.all( + minimumSize: WidgetStateProperty.all( Size(size.width * 0.5, theme.buttonTheme.height), ), - shape: MaterialStateProperty.all< - RoundedRectangleBorder>( + shape: + WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: CIRCULAR_BORDER_RADIUS, ), diff --git a/packages/smooth_app/lib/pages/user_management/sign_up_page.dart b/packages/smooth_app/lib/pages/user_management/sign_up_page.dart index d00b29c654c..8bbd6b10238 100644 --- a/packages/smooth_app/lib/pages/user_management/sign_up_page.dart +++ b/packages/smooth_app/lib/pages/user_management/sign_up_page.dart @@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:matomo_tracker/matomo_tracker.dart'; import 'package:openfoodfacts/openfoodfacts.dart'; import 'package:provider/provider.dart'; +import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/data_models/user_management_provider.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; @@ -53,13 +54,13 @@ class _SignUpPageState extends State with TraceableClientMixin { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final AppLocalizations appLocalizations = AppLocalizations.of(context); - final Size size = MediaQuery.of(context).size; + final Size size = MediaQuery.sizeOf(context); - Color getCheckBoxColor(Set states) { - const Set interactiveStates = { - MaterialState.pressed, - MaterialState.hovered, - MaterialState.focused, + Color getCheckBoxColor(Set states) { + const Set interactiveStates = { + WidgetState.pressed, + WidgetState.hovered, + WidgetState.focused, }; if (states.any(interactiveStates.contains)) { return theme.colorScheme.onSurface; @@ -235,14 +236,14 @@ class _SignUpPageState extends State with TraceableClientMixin { child: Checkbox( value: _foodProducer, fillColor: - MaterialStateProperty.resolveWith(getCheckBoxColor), + WidgetStateProperty.resolveWith(getCheckBoxColor), onChanged: (_) {}, ), ), title: Text( appLocalizations.sign_up_page_producer_checkbox, style: theme.textTheme.bodyMedium - ?.copyWith(color: theme.colorScheme.onBackground), + ?.copyWith(color: theme.colorScheme.onSurface), ), ), if (_foodProducer) ...[ @@ -273,24 +274,24 @@ class _SignUpPageState extends State with TraceableClientMixin { child: Checkbox( value: _subscribe, fillColor: - MaterialStateProperty.resolveWith(getCheckBoxColor), + WidgetStateProperty.resolveWith(getCheckBoxColor), onChanged: (_) {}, ), ), title: Text( appLocalizations.sign_up_page_subscribe_checkbox, style: theme.textTheme.bodyMedium - ?.copyWith(color: theme.colorScheme.onBackground), + ?.copyWith(color: theme.colorScheme.onSurface), ), ), const SizedBox(height: space), ElevatedButton( onPressed: () async => _signUp(), style: ButtonStyle( - minimumSize: MaterialStateProperty.all( + minimumSize: WidgetStateProperty.all( Size(size.width * 0.5, theme.buttonTheme.height + 10), ), - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( const RoundedRectangleBorder( borderRadius: CIRCULAR_BORDER_RADIUS, ), @@ -314,6 +315,7 @@ class _SignUpPageState extends State with TraceableClientMixin { } Future _signUp() async { + ///add and make complete onboarding false at signup. final AppLocalizations appLocalisations = AppLocalizations.of(context); _disagreed = !_agree; setState(() {}); @@ -411,6 +413,12 @@ class _SignUpPageState extends State with TraceableClientMixin { if (!mounted) { return; } + final UserPreferences userPreferences = + await UserPreferences.getUserPreferences(); + userPreferences.resetOnboarding(); + if (!mounted) { + return; + } await showDialog( context: context, builder: (BuildContext context) => SmoothAlertDialog( @@ -439,7 +447,7 @@ class _TermsOfUseCheckbox extends StatelessWidget { final bool agree; final bool disagree; - final MaterialPropertyResolver checkboxColorResolver; + final WidgetPropertyResolver checkboxColorResolver; final ValueChanged onCheckboxChanged; @override @@ -462,7 +470,7 @@ class _TermsOfUseCheckbox extends StatelessWidget { ignoring: true, child: Checkbox( value: agree, - fillColor: MaterialStateProperty.resolveWith( + fillColor: WidgetStateProperty.resolveWith( checkboxColorResolver, ), onChanged: (_) {}, @@ -477,7 +485,7 @@ class _TermsOfUseCheckbox extends StatelessWidget { // additional space needed because of the next text span text: '${appLocalizations.sign_up_page_agree_text} ', style: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onBackground, + color: theme.colorScheme.onSurface, ), ), TextSpan( @@ -501,7 +509,7 @@ class _TermsOfUseCheckbox extends StatelessWidget { semanticLabel: appLocalizations.termsOfUse, Icons.info, color: checkboxColorResolver( - {MaterialState.selected}, + {WidgetState.selected}, ), ), ), diff --git a/packages/smooth_app/lib/query/product_query.dart b/packages/smooth_app/lib/query/product_query.dart index 1672f7dd538..49bc51770d4 100644 --- a/packages/smooth_app/lib/query/product_query.dart +++ b/packages/smooth_app/lib/query/product_query.dart @@ -67,6 +67,13 @@ abstract class ProductQuery { final OpenFoodFactsCountry country = OpenFoodFactsCountry.fromOffTag(isoCode) ?? defaultCountry; await _setCountry(userPreferences, country); + if (userPreferences.userCurrencyCode == null) { + // very very first time, or old app with new code + final Currency? possibleCurrency = country.currency; + if (possibleCurrency != null) { + await userPreferences.setUserCurrencyCode(possibleCurrency.name); + } + } } /// Sets the global country for API queries: explicit choice by the user. diff --git a/packages/smooth_app/lib/resources/app_animations.dart b/packages/smooth_app/lib/resources/app_animations.dart index 69a8468fc3a..3086c71208b 100644 --- a/packages/smooth_app/lib/resources/app_animations.dart +++ b/packages/smooth_app/lib/resources/app_animations.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import 'package:rive/rive.dart'; +import 'package:smooth_app/cards/category_cards/svg_cache.dart'; import 'package:smooth_app/services/smooth_services.dart'; import 'package:smooth_app/themes/theme_provider.dart'; @@ -71,6 +73,57 @@ class BarcodeAnimation extends StatelessWidget { } } +class CloudUploadAnimation extends StatelessWidget { + const CloudUploadAnimation({ + required this.size, + super.key, + }) : _circleColor = null; + + const CloudUploadAnimation.circle({ + required this.size, + Color? circleColor, + super.key, + }) : _circleColor = circleColor ?? Colors.black54; + + final double size; + final Color? _circleColor; + + @override + Widget build(BuildContext context) { + Widget widget = SizedBox.square( + dimension: size, + child: RiveAnimation.direct( + AnimationsLoader.of(context), + artboard: 'Cloud upload', + animations: const ['Animation'], + ), + ); + + if (_circleColor != null) { + widget = DecoratedBox( + decoration: BoxDecoration( + color: _circleColor, + shape: BoxShape.circle, + ), + child: Padding( + padding: EdgeInsetsDirectional.only( + top: size * 0.2, + start: size * 0.2, + end: size * 0.2, + bottom: size * 0.13, + ), + child: widget, + ), + ); + } + + return SizedBox.square( + dimension: size, + child: widget, + ); + } +} + class ConsentAnimation extends StatelessWidget { const ConsentAnimation({ super.key, @@ -167,7 +220,7 @@ class _SearchEyeAnimationState extends State { @override Widget build(BuildContext context) { final double size = widget.size ?? IconTheme.of(context).size ?? 24.0; - final bool lightTheme = context.watch().isLightTheme; + final bool lightTheme = !context.watch().isDarkMode(context); return ExcludeSemantics( child: SizedBox( @@ -343,7 +396,22 @@ class _TorchAnimationState extends State { } class NutriScoreAnimation extends StatefulWidget { - const NutriScoreAnimation({ + factory NutriScoreAnimation({ + required NutriScoreValue value, + Size? size, + Key? key, + }) { + return switch (value) { + NutriScoreValue.a => NutriScoreAnimation.A(size: size, key: key), + NutriScoreValue.b => NutriScoreAnimation.B(size: size, key: key), + NutriScoreValue.c => NutriScoreAnimation.C(size: size, key: key), + NutriScoreValue.d => NutriScoreAnimation.D(size: size, key: key), + NutriScoreValue.e => NutriScoreAnimation.E(size: size, key: key), + _ => NutriScoreAnimation.unknown(size: size, key: key), + }; + } + + const NutriScoreAnimation.unknown({ this.size, super.key, }) : level = -1; @@ -374,7 +442,7 @@ class NutriScoreAnimation extends StatefulWidget { }) : level = 4; final int level; - final double? size; + final Size? size; @override State createState() => _NutriScoreAnimationState(); @@ -398,8 +466,7 @@ class _NutriScoreAnimationState extends State { /// You can test it here [https://rive.app/s/aSxao_1Mwkixud5Z2GbA5A/] void _changeNutriScoreState(int nutriScoreValue) { assert(nutriScoreValue >= -1 && nutriScoreValue <= 4); - final SMINumber currentValue = - _controller!.findInput('value')! as SMINumber; + final SMINumber currentValue = _controller!.getNumberInput('value')!; if (currentValue.value != nutriScoreValue) { currentValue.value = nutriScoreValue.toDouble(); } @@ -407,23 +474,41 @@ class _NutriScoreAnimationState extends State { @override Widget build(BuildContext context) { - final double size = widget.size ?? IconTheme.of(context).size ?? 24.0; - - return SizedBox.square( - dimension: size, - child: RiveAnimation.asset( - 'assets/animations/nutriscore.riv', - artboard: 'Nutriscore', - fit: BoxFit.cover, - onInit: (Artboard artboard) { - _controller = StateMachineController.fromArtboard( - artboard, - 'Nutriscore', - ); - - artboard.addController(_controller!); - _changeNutriScoreState(widget.level); - }, + final AppLocalizations localizations = AppLocalizations.of(context); + + return Semantics( + // TODO(g123k): Update with V2 once the animation is ready + label: switch (widget.level) { + 0 => localizations.nutriscore_a, + 1 => localizations.nutriscore_b, + 2 => localizations.nutriscore_c, + 3 => localizations.nutriscore_d, + 4 => localizations.nutriscore_e, + _ => localizations.nutriscore_unknown, + }, + image: true, + child: SizedBox.fromSize( + size: widget.size ?? + Size.fromHeight( + IconTheme.of(context).size ?? 24.0, + ), + child: AspectRatio( + aspectRatio: 176 / 94, + child: RiveAnimation.asset( + 'assets/animations/nutriscore.riv', + artboard: 'Nutriscore', + fit: BoxFit.contain, + onInit: (Artboard artboard) { + _controller = StateMachineController.fromArtboard( + artboard, + 'Nutriscore', + ); + + artboard.addController(_controller!); + _changeNutriScoreState(widget.level); + }, + ), + ), ), ); } diff --git a/packages/smooth_app/lib/resources/app_icons.dart b/packages/smooth_app/lib/resources/app_icons.dart index 37cc7ce7434..2d6dc46fdaa 100644 --- a/packages/smooth_app/lib/resources/app_icons.dart +++ b/packages/smooth_app/lib/resources/app_icons.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:smooth_app/generic_lib/design_constants.dart'; part 'app_icons_font.dart'; @@ -115,6 +114,15 @@ class Categories extends AppIcon { }) : super._(_IconsFont.categories); } +class Chicken extends AppIcon { + const Chicken({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.chicken); +} + class Check extends AppIcon { const Check({ super.color, @@ -311,6 +319,15 @@ class Expand extends AppIcon { }) : super._(_IconsFont.expand); } +class Fish extends AppIcon { + const Fish({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.fish); +} + class Fruit extends AppIcon { const Fruit({ super.color, @@ -356,6 +373,15 @@ class Ingredients extends AppIcon { }) : super._(_IconsFont.ingredients); } +class Lab extends AppIcon { + const Lab({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.lab); +} + class Labels extends AppIcon { const Labels({ super.color, @@ -374,6 +400,24 @@ class Lifebuoy extends AppIcon { }) : super._(_IconsFont.lifebuoy); } +class MagicWand extends AppIcon { + const MagicWand({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.magic_wand); +} + +class Milk extends AppIcon { + const Milk({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.milk); +} + class NoPicture extends AppIcon { const NoPicture({ super.color, @@ -451,6 +495,15 @@ class QRCode extends AppIcon { }) : super._(_IconsFont.qrcode_corners); } +class Salt extends AppIcon { + const Salt({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.salt); +} + class Share extends AppIcon { factory Share({ Color? color, @@ -515,6 +568,22 @@ class Settings extends AppIcon { }) : super._(_IconsFont.settings); } +class Soda extends AppIcon { + const Soda.happy({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.soda_happy); + + const Soda.unhappy({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.soda_unhappy); +} + class Sound extends AppIcon { const Sound.on({ super.color, @@ -531,6 +600,15 @@ class Sound extends AppIcon { }) : super._(_IconsFont.sound_off); } +class Sparkles extends AppIcon { + const Sparkles({ + super.color, + super.size, + super.shadow, + super.key, + }) : super._(_IconsFont.sparkles); +} + class Stores extends AppIcon { const Stores({ super.color, @@ -629,20 +707,58 @@ abstract class AppIcon extends StatelessWidget { @mustCallSuper Widget build(BuildContext context) { if (size == 0.0) { - return EMPTY_WIDGET; + return const SizedBox.shrink(); } + final AppIconTheme? iconTheme = AppIconTheme.maybeOf(context); final IconThemeData iconThemeData = IconTheme.of(context); final Color? color = switch (this.color) { Color _ => this.color, - _ => iconThemeData.color ?? Theme.of(context).iconTheme.color, + _ => iconTheme?.color ?? + iconThemeData.color ?? + Theme.of(context).iconTheme.color, }; - return Icon( - icon, - color: color, - size: size, - shadows: shadow != null ? [shadow!] : null, - ); + return Icon(icon, + color: color, + size: size ?? iconTheme?.size, + shadows: shadow != null + ? [shadow!] + : iconTheme?.shadow != null + ? [iconTheme!.shadow!] + : null); + } +} + +/// Allows to override the default theme of an [AppIcon] +/// If not provided, the default [IconTheme] will be used (which lacks a [shadow]) +class AppIconTheme extends InheritedWidget { + const AppIconTheme({ + super.key, + required super.child, + this.color, + this.size, + this.shadow, + }); + + final Color? color; + final double? size; + final Shadow? shadow; + + static AppIconTheme of(BuildContext context) { + final AppIconTheme? result = maybeOf(context); + assert(result != null, 'No AppIconTheme found in context'); + return result!; + } + + static AppIconTheme? maybeOf(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + @override + bool updateShouldNotify(AppIconTheme oldWidget) { + return color != oldWidget.color || + size != oldWidget.size || + shadow != oldWidget.shadow; } } diff --git a/packages/smooth_app/lib/resources/app_icons_font.dart b/packages/smooth_app/lib/resources/app_icons_font.dart index 5e76c280458..1140bedf2ed 100644 --- a/packages/smooth_app/lib/resources/app_icons_font.dart +++ b/packages/smooth_app/lib/resources/app_icons_font.dart @@ -10,10 +10,14 @@ class _IconsFont { static const String _kFontFam = 'SmoothIcons'; static const String? _kFontPkg = null; + static const IconData chicken = + IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData add_to_list = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData settings = IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData magic_wand = + IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData camera_outlined = IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData camera_filled = @@ -26,6 +30,8 @@ class _IconsFont { IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData circled_arrow = IconData(0xe809, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData lab = + IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData compare = IconData(0xe80b, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData countries = @@ -38,6 +44,8 @@ class _IconsFont { IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData expand = IconData(0xe810, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData sparkles = + IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData fruit = IconData(0xe812, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData info = @@ -86,6 +94,8 @@ class _IconsFont { IconData(0xe837, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData question = IconData(0xe838, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData salt = + IconData(0xe839, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData search = IconData(0xe83a, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData advanced_search = @@ -102,6 +112,14 @@ class _IconsFont { IconData(0xe840, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData barcode = IconData(0xe841, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData fish = + IconData(0xe842, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData milk = + IconData(0xe843, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData soda_happy = + IconData(0xe844, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData soda_unhappy = + IconData(0xe845, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData close = IconData(0xe851, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData suggestion = diff --git a/packages/smooth_app/lib/smooth_category_picker_example.dart b/packages/smooth_app/lib/smooth_category_picker_example.dart index 48287c35432..86263096ff9 100644 --- a/packages/smooth_app/lib/smooth_category_picker_example.dart +++ b/packages/smooth_app/lib/smooth_category_picker_example.dart @@ -167,9 +167,8 @@ class _ExampleAppState extends State { foregroundColor: Colors.black, ), checkboxTheme: CheckboxTheme.of(context).copyWith( - fillColor: - MaterialStateColor.resolveWith((Set states) { - if (states.contains(MaterialState.selected)) { + fillColor: WidgetStateColor.resolveWith((Set states) { + if (states.contains(WidgetState.selected)) { return Colors.green; } return Colors.black38; diff --git a/packages/smooth_app/lib/themes/color_schemes.dart b/packages/smooth_app/lib/themes/color_schemes.dart index 9e31f3135f9..59bd71c4963 100644 --- a/packages/smooth_app/lib/themes/color_schemes.dart +++ b/packages/smooth_app/lib/themes/color_schemes.dart @@ -12,10 +12,8 @@ const ColorScheme lightColorScheme = ColorScheme( onSecondary: Color(0xFF000000), error: Color(0xFFEB5757), onError: Color(0xFFFFFFFF), - background: Color(0xFFFFFFFF), - onBackground: Color(0xFF000000), - surface: Color(0xFF85746C), - onSurface: Color(0xFFFFFFFF), + surface: Color(0xFFFFFFFF), + onSurface: Color(0xFF000000), ); const ColorScheme darkColorScheme = ColorScheme( @@ -27,10 +25,8 @@ const ColorScheme darkColorScheme = ColorScheme( onSecondary: Color(0xFFFFFFFF), error: Color(0xFFEB5757), onError: Color(0xFFFFFFFF), - background: Color(0xFF201A17), - onBackground: Color(0xFFFFFFFF), - surface: Color(0xFFEDE0DB), - onSurface: Color(0xFF000000), + surface: Color(0xFF201A17), + onSurface: Color(0xFFFFFFFF), ); const ColorScheme trueDarkColorScheme = ColorScheme( @@ -42,10 +38,8 @@ const ColorScheme trueDarkColorScheme = ColorScheme( onSecondary: Color(0xFFE1E1E1), error: Color(0xFFEA2B2B), onError: Color(0xFFE1E1E1), - background: Color(0xFF000000), - onBackground: Color(0xFFE1E1E1), surface: Color(0xFF000000), - onSurface: Color(0xFFE1E1E1), + onSurface: Color(0xFFFFFFFF), ); const String CONTRAST_LOW = 'Low'; diff --git a/packages/smooth_app/lib/themes/smooth_theme.dart b/packages/smooth_app/lib/themes/smooth_theme.dart index 37017aef0a2..1d41a28ecbe 100644 --- a/packages/smooth_app/lib/themes/smooth_theme.dart +++ b/packages/smooth_app/lib/themes/smooth_theme.dart @@ -4,6 +4,7 @@ import 'package:smooth_app/generic_lib/design_constants.dart'; import 'package:smooth_app/themes/color_provider.dart'; import 'package:smooth_app/themes/color_schemes.dart'; import 'package:smooth_app/themes/contrast_provider.dart'; +import 'package:smooth_app/themes/smooth_theme_colors.dart'; import 'package:smooth_app/themes/theme_provider.dart'; class SmoothTheme { @@ -39,10 +40,14 @@ class SmoothTheme { return ThemeData( useMaterial3: false, + fontFamily: 'OpenSans', primaryColor: DARK_BROWN_COLOR, + extensions: [ + SmoothColorsThemeExtension.defaultValues(), + ], colorScheme: myColorScheme, canvasColor: themeProvider.currentTheme == THEME_AMOLED - ? myColorScheme.background + ? myColorScheme.surface : null, bottomNavigationBarTheme: BottomNavigationBarThemeData( selectedIconTheme: const IconThemeData(size: 24.0), @@ -56,11 +61,10 @@ class SmoothTheme { ), elevatedButtonTheme: ElevatedButtonThemeData( style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) => - states.contains(MaterialState.disabled) - ? Colors.grey - : myColorScheme.primary, + backgroundColor: WidgetStateProperty.resolveWith( + (Set states) => states.contains(WidgetState.disabled) + ? Colors.grey + : myColorScheme.primary, ), ), ), @@ -71,8 +75,8 @@ class SmoothTheme { ? getTextTheme(themeProvider, textContrastProvider) : _TEXT_THEME, appBarTheme: AppBarTheme( - color: myColorScheme.background, - foregroundColor: myColorScheme.onBackground, + color: myColorScheme.surface, + foregroundColor: myColorScheme.onSurface, systemOverlayStyle: SystemUiOverlayStyle.light, ), dividerColor: const Color(0xFFdfdfdf), @@ -80,59 +84,59 @@ class SmoothTheme { fillColor: myColorScheme.secondary, ), iconTheme: IconThemeData( - color: myColorScheme.onBackground, + color: myColorScheme.onSurface, ), snackBarTheme: SnackBarThemeData( contentTextStyle: _TEXT_THEME.bodyMedium?.copyWith(color: myColorScheme.onPrimary), actionTextColor: myColorScheme.onPrimary, - backgroundColor: myColorScheme.onBackground, + backgroundColor: myColorScheme.onSurface, ), bannerTheme: MaterialBannerThemeData( contentTextStyle: TextStyle(color: myColorScheme.onSecondary), backgroundColor: myColorScheme.secondary, ), checkboxTheme: CheckboxThemeData( - fillColor: MaterialStateProperty.resolveWith( - (Set states) { - if (states.contains(MaterialState.disabled)) { + fillColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { return null; } - if (states.contains(MaterialState.selected)) { + if (states.contains(WidgetState.selected)) { return myColorScheme.primary; } return null; }), ), radioTheme: RadioThemeData( - fillColor: MaterialStateProperty.resolveWith( - (Set states) { - if (states.contains(MaterialState.disabled)) { + fillColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { return null; } - if (states.contains(MaterialState.selected)) { + if (states.contains(WidgetState.selected)) { return myColorScheme.primary; } return null; }), ), switchTheme: SwitchThemeData( - thumbColor: MaterialStateProperty.resolveWith( - (Set states) { - if (states.contains(MaterialState.disabled)) { + thumbColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { return null; } - if (states.contains(MaterialState.selected)) { + if (states.contains(WidgetState.selected)) { return myColorScheme.primary; } return null; }), - trackColor: MaterialStateProperty.resolveWith( - (Set states) { - if (states.contains(MaterialState.disabled)) { + trackColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { return null; } - if (states.contains(MaterialState.selected)) { + if (states.contains(WidgetState.selected)) { return myColorScheme.primary; } return null; diff --git a/packages/smooth_app/lib/themes/smooth_theme_colors.dart b/packages/smooth_app/lib/themes/smooth_theme_colors.dart new file mode 100644 index 00000000000..76637bd70e0 --- /dev/null +++ b/packages/smooth_app/lib/themes/smooth_theme_colors.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; + +class SmoothColorsThemeExtension + extends ThemeExtension { + SmoothColorsThemeExtension({ + required this.primaryUltraBlack, + required this.primaryBlack, + required this.primaryDark, + required this.primarySemiDark, + required this.primaryNormal, + required this.primaryMedium, + required this.primaryLight, + required this.secondaryNormal, + required this.secondaryLight, + required this.green, + required this.orange, + required this.red, + required this.greyDark, + required this.greyLight, + }); + + SmoothColorsThemeExtension.defaultValues() + : primaryUltraBlack = const Color(0xFF52443D), + primaryBlack = const Color(0xFF341100), + primaryDark = const Color(0xFF483527), + primarySemiDark = const Color(0xFF52443D), + primaryNormal = const Color(0xFFA08D84), + primaryMedium = const Color(0xFFEDE0DB), + primaryLight = const Color(0xFFF6F3F0), + secondaryNormal = const Color(0xFFF2994A), + secondaryLight = const Color(0xFFEE8858), + green = const Color(0xFF219653), + orange = const Color(0xFFFB8229), + red = const Color(0xFFEB5757), + greyDark = const Color(0xFF666666), + greyLight = const Color(0xFF8F8F8F); + + // Ristreto + final Color primaryUltraBlack; + // Chocolate + final Color primaryBlack; + // Cortado + final Color primaryDark; + // Mocha + final Color primarySemiDark; + // Macchiato + final Color primaryNormal; + // Cappuccino + final Color primaryMedium; + // Latte + final Color primaryLight; + final Color secondaryNormal; + final Color secondaryLight; + final Color green; + final Color orange; + final Color red; + final Color greyDark; + final Color greyLight; + + @override + ThemeExtension copyWith({ + Color? primaryUltraBlack, + Color? primaryBlack, + Color? primaryDark, + Color? primarySemiDark, + Color? primaryNormal, + Color? primaryMedium, + Color? primaryLight, + Color? secondaryNormal, + Color? secondaryLight, + Color? green, + Color? orange, + Color? red, + Color? greyDark, + Color? greyLight, + }) { + return SmoothColorsThemeExtension( + primaryUltraBlack: primaryUltraBlack ?? this.primaryUltraBlack, + primaryBlack: primaryBlack ?? this.primaryBlack, + primaryDark: primaryDark ?? this.primaryDark, + primarySemiDark: primarySemiDark ?? this.primarySemiDark, + primaryNormal: primaryNormal ?? this.primaryNormal, + primaryMedium: primaryMedium ?? this.primaryMedium, + primaryLight: primaryLight ?? this.primaryLight, + secondaryNormal: secondaryNormal ?? this.secondaryNormal, + secondaryLight: secondaryLight ?? this.secondaryLight, + green: green ?? this.green, + orange: orange ?? this.orange, + red: red ?? this.red, + greyDark: greyDark ?? this.greyDark, + greyLight: greyLight ?? this.greyLight, + ); + } + + @override + ThemeExtension lerp( + covariant ThemeExtension? other, + double t, + ) { + if (other is! SmoothColorsThemeExtension) { + return this; + } + + return SmoothColorsThemeExtension( + primaryUltraBlack: Color.lerp( + primaryUltraBlack, + other.primaryUltraBlack, + t, + )!, + primaryBlack: Color.lerp( + primaryBlack, + other.primaryBlack, + t, + )!, + primaryDark: Color.lerp( + primaryDark, + other.primaryDark, + t, + )!, + primarySemiDark: Color.lerp( + primarySemiDark, + other.primarySemiDark, + t, + )!, + primaryNormal: Color.lerp( + primaryNormal, + other.primaryNormal, + t, + )!, + primaryMedium: Color.lerp( + primaryMedium, + other.primaryMedium, + t, + )!, + primaryLight: Color.lerp( + primaryLight, + other.primaryLight, + t, + )!, + secondaryNormal: Color.lerp( + secondaryNormal, + other.secondaryNormal, + t, + )!, + secondaryLight: Color.lerp( + secondaryLight, + other.secondaryLight, + t, + )!, + green: Color.lerp( + green, + other.green, + t, + )!, + orange: Color.lerp( + orange, + other.orange, + t, + )!, + red: Color.lerp( + red, + other.red, + t, + )!, + greyDark: Color.lerp( + greyDark, + other.greyDark, + t, + )!, + greyLight: Color.lerp( + greyLight, + other.greyLight, + t, + )!, + ); + } +} diff --git a/packages/smooth_app/lib/themes/theme_provider.dart b/packages/smooth_app/lib/themes/theme_provider.dart index 3f0626aee01..08862fb23ba 100644 --- a/packages/smooth_app/lib/themes/theme_provider.dart +++ b/packages/smooth_app/lib/themes/theme_provider.dart @@ -7,15 +7,28 @@ const String THEME_DARK = 'Dark'; const String THEME_AMOLED = 'AMOLED'; class ThemeProvider with ChangeNotifier { - ThemeProvider(this._userPreferences); + ThemeProvider(this._userPreferences) + : _theme = _userPreferences.currentTheme { + _userPreferences.addListener(_onPreferencesChanged); + } final UserPreferences _userPreferences; // The onboarding needs the light mode. bool _forceLight = false; - String get currentTheme => - _forceLight ? THEME_LIGHT : _userPreferences.currentTheme; + // Local cache for [_userPreferences.currentTheme] + String _theme; + + void _onPreferencesChanged() { + final String newTheme = _userPreferences.currentTheme; + if (newTheme != _theme) { + _theme = newTheme; + notifyListeners(); + } + } + + String get currentTheme => _forceLight ? THEME_LIGHT : _theme; void setOnboardingComplete(final bool onboardingComplete) { _forceLight = !onboardingComplete; @@ -53,6 +66,15 @@ class ThemeProvider with ChangeNotifier { } bool isDarkMode(BuildContext context) { - return MediaQuery.platformBrightnessOf(context) == Brightness.dark; + if (currentTheme == THEME_SYSTEM_DEFAULT) { + return MediaQuery.platformBrightnessOf(context) == Brightness.dark; + } + return [THEME_DARK, THEME_AMOLED].contains(currentTheme); + } + + @override + void dispose() { + _userPreferences.removeListener(_onPreferencesChanged); + super.dispose(); } } diff --git a/packages/smooth_app/lib/widgets/attribute_button.dart b/packages/smooth_app/lib/widgets/attribute_button.dart index 51061d9847a..a91b5785dcd 100644 --- a/packages/smooth_app/lib/widgets/attribute_button.dart +++ b/packages/smooth_app/lib/widgets/attribute_button.dart @@ -30,7 +30,7 @@ class AttributeButton extends StatelessWidget { productPreferences.getImportanceIdForAttributeId(attribute.id!); const double horizontalPadding = LARGE_SPACE; final double widgetWidth = - MediaQuery.of(context).size.width - 2 * horizontalPadding; + MediaQuery.sizeOf(context).width - 2 * horizontalPadding; final double importanceWidth = widgetWidth / 4; final TextStyle style = themeData.textTheme.headlineMedium!; final String? info = attribute.settingNote; diff --git a/packages/smooth_app/lib/widgets/ranking_floating_action_button.dart b/packages/smooth_app/lib/widgets/ranking_floating_action_button.dart index b12bf717edd..7a2de39e52a 100644 --- a/packages/smooth_app/lib/widgets/ranking_floating_action_button.dart +++ b/packages/smooth_app/lib/widgets/ranking_floating_action_button.dart @@ -1,8 +1,8 @@ -import 'dart:math'; - +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:smooth_app/generic_lib/animations/smooth_reveal_animation.dart'; +import 'package:smooth_app/generic_lib/design_constants.dart'; // TODO(monsieurtanuki): we should probably remove that class to avoid confusion with the "compare" button /// Floating Action Button dedicated to Personal Ranking @@ -19,19 +19,31 @@ class RankingFloatingActionButton extends StatelessWidget { Widget build(BuildContext context) => SmoothRevealAnimation( animationCurve: Curves.easeInOutBack, startOffset: const Offset(0.0, 1.0), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox(width: MediaQuery.of(context).size.width * 0.09), - FloatingActionButton.extended( - heroTag: 'ranking_fab_${Random(100)}', - elevation: 12.0, - icon: const Icon(rankingIconData), - label: Text(AppLocalizations.of(context).myPersonalizedRanking), + child: Container( + height: MINIMUM_TOUCH_SIZE, + margin: + EdgeInsets.only(left: MediaQuery.sizeOf(context).width * 0.09), + alignment: Alignment.center, + child: SizedBox( + height: MINIMUM_TOUCH_SIZE, + child: ElevatedButton.icon( onPressed: onPressed, + style: ButtonStyle( + shape: WidgetStateProperty.all( + const RoundedRectangleBorder( + borderRadius: CIRCULAR_BORDER_RADIUS, + ), + ), + ), + icon: const Icon(rankingIconData), + label: AutoSizeText( + AppLocalizations.of(context).myPersonalizedRanking, + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), ), - ], + ), ), ); } diff --git a/packages/smooth_app/lib/widgets/smooth_product_carousel.dart b/packages/smooth_app/lib/widgets/smooth_product_carousel.dart deleted file mode 100644 index 4120b5b9ebc..00000000000 --- a/packages/smooth_app/lib/widgets/smooth_product_carousel.dart +++ /dev/null @@ -1,544 +0,0 @@ -import 'dart:math'; - -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:carousel_slider/carousel_slider.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:provider/provider.dart'; -import 'package:scanner_shared/scanner_shared.dart' hide EMPTY_WIDGET; -import 'package:smooth_app/cards/product_cards/smooth_product_base_card.dart'; -import 'package:smooth_app/cards/product_cards/smooth_product_card_error.dart'; -import 'package:smooth_app/cards/product_cards/smooth_product_card_loading.dart'; -import 'package:smooth_app/cards/product_cards/smooth_product_card_not_found.dart'; -import 'package:smooth_app/cards/product_cards/smooth_product_card_thanks.dart'; -import 'package:smooth_app/data_models/continuous_scan_model.dart'; -import 'package:smooth_app/data_models/preferences/user_preferences.dart'; -import 'package:smooth_app/data_models/tagline.dart'; -import 'package:smooth_app/generic_lib/design_constants.dart'; -import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; -import 'package:smooth_app/helpers/app_helper.dart'; -import 'package:smooth_app/helpers/camera_helper.dart'; -import 'package:smooth_app/helpers/launch_url_helper.dart'; -import 'package:smooth_app/helpers/user_feedback_helper.dart'; -import 'package:smooth_app/pages/carousel_manager.dart'; -import 'package:smooth_app/pages/navigator/app_navigator.dart'; -import 'package:smooth_app/pages/preferences/user_preferences_widgets.dart'; -import 'package:smooth_app/pages/scan/scan_product_card_loader.dart'; -import 'package:smooth_app/pages/scan/search_page.dart'; -import 'package:smooth_app/services/smooth_services.dart'; - -class SmoothProductCarousel extends StatefulWidget { - const SmoothProductCarousel({ - this.containSearchCard = false, - this.onPageChangedTo, - }); - - final bool containSearchCard; - final Function(int page, String? productBarcode)? onPageChangedTo; - - @override - State createState() => _SmoothProductCarouselState(); -} - -class _SmoothProductCarouselState extends State { - static const double HORIZONTAL_SPACE_BETWEEN_CARDS = 5.0; - - List barcodes = []; - String? _lastConsultedBarcode; - int? _carrouselMovingTo; - int _lastIndex = 0; - - int get _searchCardAdjustment => widget.containSearchCard ? 1 : 0; - late ContinuousScanModel _model; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _model = context.watch(); - - if (!ExternalCarouselManager.read(context).controller.ready) { - return; - } - - barcodes = _model.getBarcodes(); - - if (barcodes.isEmpty) { - // Ensure to reset all variables - _lastConsultedBarcode = null; - _carrouselMovingTo = null; - _lastIndex = 0; - return; - } else if (_lastConsultedBarcode == _model.latestConsultedBarcode) { - // Prevent multiple irrelevant movements - return; - } - - _lastConsultedBarcode = _model.latestConsultedBarcode; - final int cardsCount = barcodes.length + _searchCardAdjustment; - - if (_model.latestConsultedBarcode != null && - _model.latestConsultedBarcode!.isNotEmpty) { - final int indexBarcode = barcodes.indexOf(_model.latestConsultedBarcode!); - if (indexBarcode >= 0) { - final int indexCarousel = indexBarcode + _searchCardAdjustment; - _moveControllerTo(indexCarousel); - } else { - if (_lastIndex > cardsCount) { - _moveControllerTo(cardsCount); - } else { - _moveControllerTo(_lastIndex); - } - } - } else { - _moveControllerTo(0); - } - } - - Future _moveControllerTo(int page) async { - if (_carrouselMovingTo == null && _lastIndex != page) { - widget.onPageChangedTo?.call( - page, - page >= _searchCardAdjustment - ? barcodes[page - _searchCardAdjustment] - : null, - ); - - _carrouselMovingTo = page; - ExternalCarouselManager.read(context).animatePageTo(page); - _carrouselMovingTo = null; - } - } - - @override - Widget build(BuildContext context) { - barcodes = _model.getBarcodes(); - - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return CarouselSlider.builder( - itemCount: barcodes.length + _searchCardAdjustment, - itemBuilder: - (BuildContext context, int itemIndex, int itemRealIndex) { - return SizedBox.expand( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: HORIZONTAL_SPACE_BETWEEN_CARDS, - ), - child: widget.containSearchCard && itemIndex == 0 - ? SearchCard(height: constraints.maxHeight) - : _getWidget(itemIndex - _searchCardAdjustment), - ), - ); - }, - carouselController: ExternalCarouselManager.watch(context).controller, - options: CarouselOptions( - enlargeCenterPage: false, - viewportFraction: _computeViewPortFraction(), - height: constraints.maxHeight, - enableInfiniteScroll: false, - onPageChanged: (int index, CarouselPageChangedReason reason) { - _lastIndex = index; - - if (index > 0) { - if (reason == CarouselPageChangedReason.manual) { - _model.lastConsultedBarcode = - barcodes[index - _searchCardAdjustment]; - _lastConsultedBarcode = _model.latestConsultedBarcode; - } - } else if (index == 0) { - _model.lastConsultedBarcode = null; - _lastConsultedBarcode = null; - } - }, - ), - ); - }, - ); - } - - /// Displays the card for this [index] of a list of [barcodes] - /// - /// There are special cases when the item display is refreshed - /// after the product disappeared and before the whole carousel is refreshed. - /// In those cases, we don't want the app to crash and display a Container - /// instead in the meanwhile. - Widget _getWidget(final int index) { - if (index >= barcodes.length) { - return EMPTY_WIDGET; - } - final String barcode = barcodes[index]; - switch (_model.getBarcodeState(barcode)!) { - case ScannedProductState.FOUND: - case ScannedProductState.CACHED: - return ScanProductCardLoader(barcode); - case ScannedProductState.LOADING: - return SmoothProductCardLoading( - barcode: barcode, - onRemoveProduct: (_) => _model.removeBarcode(barcode), - ); - case ScannedProductState.NOT_FOUND: - return SmoothProductCardNotFound( - barcode: barcode, - onAddProduct: () async { - await _model.refresh(); - setState(() {}); - }, - onRemoveProduct: (_) => _model.removeBarcode(barcode), - ); - case ScannedProductState.THANKS: - return const SmoothProductCardThanks(); - case ScannedProductState.ERROR_INTERNET: - return SmoothProductCardError( - barcode: barcode, - errorType: ScannedProductState.ERROR_INTERNET, - ); - case ScannedProductState.ERROR_INVALID_CODE: - return SmoothProductCardError( - barcode: barcode, - errorType: ScannedProductState.ERROR_INVALID_CODE, - ); - } - } - - double _computeViewPortFraction() { - final double screenWidth = MediaQuery.of(context).size.width; - return (screenWidth - - (SmoothBarcodeScannerVisor.CORNER_PADDING * 2) - - (SmoothBarcodeScannerVisor.STROKE_WIDTH * 2) + - (HORIZONTAL_SPACE_BETWEEN_CARDS * 4)) / - screenWidth; - } -} - -class SearchCard extends StatelessWidget { - const SearchCard({required this.height}); - - final double height; - - static const double OPACITY = 0.85; - - @override - Widget build(BuildContext context) { - final AppLocalizations localizations = AppLocalizations.of(context); - - return SmoothProductBaseCard( - backgroundColorOpacity: OPACITY, - margin: const EdgeInsets.symmetric( - vertical: VERY_SMALL_SPACE, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SvgPicture.asset( - Theme.of(context).brightness == Brightness.light - ? 'assets/app/release_icon_light_transparent_no_border.svg' - : 'assets/app/release_icon_dark_transparent_no_border.svg', - width: height * 0.2, - height: height * 0.2, - package: AppHelper.APP_PACKAGE, - ), - Padding( - padding: const EdgeInsets.only(top: MEDIUM_SPACE), - child: AutoSizeText( - localizations.welcomeToOpenFoodFacts, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 26.0, - fontWeight: FontWeight.bold, - height: 1.00, - ), - maxLines: 1, - ), - ), - const Expanded(child: _SearchCardContent()), - ], - ), - ); - } -} - -class _SearchCardContent extends StatefulWidget { - const _SearchCardContent({ - Key? key, - }) : super(key: key); - - @override - State<_SearchCardContent> createState() => _SearchCardContentState(); -} - -class _SearchCardContentState extends State<_SearchCardContent> - with AutomaticKeepAliveClientMixin { - late _SearchCardContentType _content; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - - final UserPreferences preferences = context.read(); - final int scans = preferences.numberOfScans; - if (CameraHelper.hasACamera && scans < 1) { - _content = _SearchCardContentType.DEFAULT; - } else if (!preferences.inAppReviewAlreadyAsked && - Random().nextInt(10) == 0) { - _content = _SearchCardContentType.REVIEW_APP; - } else { - _content = _SearchCardContentType.TAG_LINE; - } - } - - @override - Widget build(BuildContext context) { - super.build(context); - final ThemeData themeData = Theme.of(context); - final bool darkMode = themeData.brightness == Brightness.dark; - - return Padding( - padding: const EdgeInsets.symmetric(vertical: VERY_SMALL_SPACE), - child: DefaultTextStyle.merge( - style: const TextStyle( - fontSize: LARGE_SPACE, - height: 1.22, - ), - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - maxLines: 5, - child: Column( - children: [ - Expanded( - child: switch (_content) { - _SearchCardContentType.DEFAULT => - const _SearchCardContentDefault(), - _SearchCardContentType.TAG_LINE => - const _SearchCardContentTagLine(), - _SearchCardContentType.REVIEW_APP => - _SearchCardContentAppReview( - onHideReview: () { - setState(() => _content = _SearchCardContentType.DEFAULT); - }, - ), - }, - ), - if (_content != _SearchCardContentType.REVIEW_APP) - SearchField( - onFocus: () => _openSearchPage(context), - readOnly: true, - showClearButton: false, - backgroundColor: darkMode - ? Colors.white10 - : const Color.fromARGB(255, 240, 240, 240) - .withOpacity(SearchCard.OPACITY), - foregroundColor: themeData.colorScheme.onSurface - .withOpacity(SearchCard.OPACITY), - ), - ], - ), - ), - ); - } - - void _openSearchPage(BuildContext context) { - AppNavigator.of(context).push(AppRoutes.SEARCH); - } - - @override - bool get wantKeepAlive => true; -} - -enum _SearchCardContentType { - TAG_LINE, - REVIEW_APP, - DEFAULT, -} - -class _SearchCardContentDefault extends StatelessWidget { - const _SearchCardContentDefault({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final AppLocalizations localizations = AppLocalizations.of(context); - - return Center( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10.0, - ), - child: AutoSizeText( - localizations.searchPanelHeader, - ), - ), - ); - } -} - -class _SearchCardContentTagLine extends StatelessWidget { - const _SearchCardContentTagLine(); - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: fetchTagLine(), - builder: (BuildContext context, AsyncSnapshot data) { - if (data.data != null) { - final TagLineItem tagLine = data.data!; - return InkWell( - borderRadius: ANGULAR_BORDER_RADIUS, - onTap: tagLine.hasLink - ? () async => LaunchUrlHelper.launchURL(tagLine.url) - : null, - child: Center( - child: AutoSizeText( - tagLine.message, - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - ), - ), - ); - } else { - return const _SearchCardContentDefault(); - } - }, - ); - } -} - -class _SearchCardContentAppReview extends StatelessWidget { - const _SearchCardContentAppReview({ - required this.onHideReview, - }); - - final VoidCallback onHideReview; - - @override - Widget build(BuildContext context) { - final AppLocalizations localizations = AppLocalizations.of(context); - final UserPreferences preferences = context.read(); - - return Center( - child: OutlinedButtonTheme( - data: OutlinedButtonThemeData( - style: OutlinedButton.styleFrom( - shape: const RoundedRectangleBorder( - borderRadius: ROUNDED_BORDER_RADIUS, - ), - side: BorderSide( - color: Theme.of(context).colorScheme.primary, - ), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Spacer(), - const UserPreferencesListItemDivider( - margin: EdgeInsetsDirectional.only( - top: MEDIUM_SPACE, - bottom: SMALL_SPACE, - ), - ), - AutoSizeText( - localizations.tagline_app_review, - style: const TextStyle( - fontSize: 16.0, - ), - ), - const SizedBox(height: SMALL_SPACE), - SizedBox( - width: double.infinity, - child: OutlinedButton( - onPressed: () async { - if (await ApplicationStore.openAppReview()) { - await preferences.markInAppReviewAsShown(); - onHideReview.call(); - } - }, - style: OutlinedButton.styleFrom( - padding: const EdgeInsetsDirectional.symmetric( - vertical: SMALL_SPACE, - ), - ), - child: Text( - localizations.tagline_app_review_button_positive, - style: const TextStyle(fontSize: 17.0), - textAlign: TextAlign.center, - ), - ), - ), - const SizedBox(height: VERY_SMALL_SPACE), - IntrinsicHeight( - child: Row( - children: [ - Expanded( - child: OutlinedButton( - onPressed: () async { - preferences.markInAppReviewAsShown(); - await _showNegativeDialog(context, localizations); - onHideReview(); - }, - child: Text( - localizations.tagline_app_review_button_negative, - textAlign: TextAlign.center, - ), - ), - ), - const SizedBox(width: VERY_SMALL_SPACE), - Expanded( - child: OutlinedButton( - onPressed: () => onHideReview(), - child: Text( - localizations.tagline_app_review_button_later, - textAlign: TextAlign.center, - ), - ), - ), - ], - ), - ), - const Spacer(), - ], - ), - ), - ); - } - - Future _showNegativeDialog( - BuildContext context, - AppLocalizations localizations, - ) { - return showDialog( - context: context, - builder: (BuildContext context) { - return SmoothAlertDialog( - title: localizations.app_review_negative_modal_title, - body: Padding( - padding: const EdgeInsetsDirectional.only( - start: SMALL_SPACE, - end: SMALL_SPACE, - bottom: MEDIUM_SPACE, - ), - child: Text( - localizations.app_review_negative_modal_text, - textAlign: TextAlign.center, - ), - ), - positiveAction: SmoothActionButton( - text: localizations.app_review_negative_modal_positive_button, - onPressed: () { - final String formLink = UserFeedbackHelper.getFeedbackFormLink(); - LaunchUrlHelper.launchURL(formLink); - Navigator.of(context).pop(); - }, - ), - negativeAction: SmoothActionButton( - text: localizations.app_review_negative_modal_negative_button, - onPressed: () => Navigator.of(context).pop(), - ), - actionsAxis: Axis.vertical, - ); - }, - ); - } -} diff --git a/packages/smooth_app/lib/widgets/smooth_scaffold.dart b/packages/smooth_app/lib/widgets/smooth_scaffold.dart index 1943347dcd6..66e355fa3d0 100644 --- a/packages/smooth_app/lib/widgets/smooth_scaffold.dart +++ b/packages/smooth_app/lib/widgets/smooth_scaffold.dart @@ -162,17 +162,20 @@ class SmoothScaffoldState extends ScaffoldState { switch (brightness) { case Brightness.dark: - return const SystemUiOverlayStyle( + return SystemUiOverlayStyle( statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, - systemNavigationBarContrastEnforced: false, + systemNavigationBarContrastEnforced: + !Platform.isAndroid ? false : null, ); + case Brightness.light: default: - return const SystemUiOverlayStyle( + return SystemUiOverlayStyle( statusBarIconBrightness: Brightness.light, statusBarBrightness: Brightness.dark, - systemNavigationBarContrastEnforced: false, + systemNavigationBarContrastEnforced: + !Platform.isAndroid ? false : null, ); } } diff --git a/packages/smooth_app/lib/widgets/smooth_text.dart b/packages/smooth_app/lib/widgets/smooth_text.dart index 6447bf15e7b..bfa2f17066c 100644 --- a/packages/smooth_app/lib/widgets/smooth_text.dart +++ b/packages/smooth_app/lib/widgets/smooth_text.dart @@ -121,7 +121,7 @@ class TextHighlighter extends StatelessWidget { final String textWithoutDiacritics = text.getComparisonSafeString(); final Iterable highlightedParts = - RegExp(filterWithoutDiacritics.trim()).allMatches( + RegExp(RegExp.escape(filterWithoutDiacritics.trim())).allMatches( textWithoutDiacritics, ); @@ -190,3 +190,31 @@ class TextHighlighter extends StatelessWidget { return endPosition + diff; } } + +class HighlightedTextSpan extends WidgetSpan { + HighlightedTextSpan({ + required String text, + required TextStyle textStyle, + required EdgeInsetsGeometry padding, + required Color backgroundColor, + required double radius, + EdgeInsetsGeometry? margin, + }) : assert(radius > 0.0), + super( + alignment: PlaceholderAlignment.middle, + child: Container( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.all( + Radius.circular(radius), + ), + ), + margin: margin, + padding: padding, + child: Text( + text, + style: textStyle, + ), + ), + ); +} diff --git a/packages/smooth_app/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/smooth_app/macos/Flutter/GeneratedPluginRegistrant.swift index aa4df10f2a5..89d3e87fb16 100644 --- a/packages/smooth_app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/packages/smooth_app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -24,7 +24,7 @@ import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) - ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin")) diff --git a/packages/smooth_app/macos/Podfile.lock b/packages/smooth_app/macos/Podfile.lock index 72808270949..b35a6160333 100644 --- a/packages/smooth_app/macos/Podfile.lock +++ b/packages/smooth_app/macos/Podfile.lock @@ -108,23 +108,23 @@ SPEC CHECKSUMS: audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f - file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 + file_selector_macos: 54fdab7caa3ac3fc43c9fac4d7d8d231277f8cf2 flutter_image_compress_macos: c26c3c13ea0f28ae6dea4e139b3292e7729f99f1 - flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea + flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0 mobile_scanner: 621cf2c34e1c74ae7ce5c6793638ab600723bdea package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 ReachabilitySwift: 2128f3a8c9107e1ad33574c6e58e8285d460b149 - rive_common: 90d667125afd74095b697cf7d2d2e9c50813ec8c + rive_common: cf5ab646aa576b2d742d0e2d528126fbf032c856 Sentry: ebc12276bd17613a114ab359074096b6b3725203 sentry_flutter: dff1df05dc39c83d04f9330b36360fc374574c5e SentryPrivate: d651efb234cf385ec9a1cdd3eff94b5e78a0e0fe share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 - shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec - url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 + url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 PODFILE CHECKSUM: 0d3963a09fc94f580682bd88480486da345dc3f0 diff --git a/packages/smooth_app/pubspec.lock b/packages/smooth_app/pubspec.lock index d84978fdf79..c72b0a139fa 100644 --- a/packages/smooth_app/pubspec.lock +++ b/packages/smooth_app/pubspec.lock @@ -65,10 +65,10 @@ packages: dependency: transitive description: name: archive - sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "3.4.10" + version: "3.6.1" args: dependency: transitive description: @@ -161,10 +161,10 @@ packages: dependency: transitive description: name: barcode - sha256: "1fe4a55344505850517ce72d4a3a7b9ccf51b0dc1631ee7e552f6eacc4947f96" + sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003 url: "https://pub.dev" source: hosted - version: "2.2.7" + version: "2.2.8" barcode_widget: dependency: "direct main" description: @@ -209,26 +209,26 @@ packages: dependency: "direct main" description: name: camera - sha256: "9499cbc2e51d8eb0beadc158b288380037618ce4e30c9acbc4fae1ac3ecb5797" + sha256: dfa8fc5a1adaeb95e7a54d86a5bd56f4bb0e035515354c8ac6d262e35cec2ec8 url: "https://pub.dev" source: hosted - version: "0.10.5+9" + version: "0.10.6" camera_android: dependency: transitive description: name: camera_android - sha256: "7b0aba6398afa8475e2bc9115d976efb49cf8db781e922572d443795c04a4f4f" + sha256: b350ac087f111467e705b2b76cc1322f7f5bdc122aa83b4b243b0872f390d229 url: "https://pub.dev" source: hosted - version: "0.10.9+1" + version: "0.10.9+2" camera_avfoundation: dependency: transitive description: name: camera_avfoundation - sha256: "9dbbb253aaf201a69c40cf95571f366ca936305d2de012684e21f6f1b1433d31" + sha256: "7d021e8cd30d9b71b8b92b4ad669e80af432d722d18d6aac338572754a786c15" url: "https://pub.dev" source: hosted - version: "0.9.15+4" + version: "0.9.16" camera_platform_interface: dependency: transitive description: @@ -305,18 +305,18 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: db7a4e143dc72cc3cb2044ef9b052a7ebfe729513e6a82943bc3526f784365b8 + sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "5.0.2" connectivity_plus_platform_interface: dependency: transitive description: name: connectivity_plus_platform_interface - sha256: b6a56efe1e6675be240de39107281d4034b64ac23438026355b4234042a35adb + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.2.4" convert: dependency: transitive description: @@ -329,10 +329,10 @@ packages: dependency: "direct main" description: name: crop_image - sha256: "67d379ea927f4a9e48bf3d314a74bff7d86cdefd4a58af4554b37daa3c10bb9c" + sha256: "6cf20655ecbfba99c369d43ec7adcfa49bf135af88fb75642173d6224a95d3f1" url: "https://pub.dev" source: hosted - version: "1.0.12" + version: "1.0.13" cross_file: dependency: transitive description: @@ -397,14 +397,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" - device_frame: - dependency: transitive - description: - name: device_frame - sha256: afe76182aec178d171953d9b4a50a43c57c7cf3c77d8b09a48bf30c8fa04dd9d - url: "https://pub.dev" - source: hosted - version: "1.1.0" device_info_plus: dependency: "direct main" description: @@ -421,14 +413,6 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" - device_preview: - dependency: "direct main" - description: - name: device_preview - sha256: "2f097bf31b929e15e6756dbe0ec1bcb63952ab9ed51c25dc5a2c722d2b21fdaf" - url: "https://pub.dev" - source: hosted - version: "1.1.0" diacritic: dependency: "direct main" description: @@ -481,10 +465,10 @@ packages: dependency: transitive description: name: file_selector_macos - sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 + sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385 url: "https://pub.dev" source: hosted - version: "0.9.3+3" + version: "0.9.4" file_selector_platform_interface: dependency: transitive description: @@ -542,34 +526,34 @@ packages: dependency: transitive description: name: flutter_custom_tabs_android - sha256: "5701a3e38117dfc59e5fa9e84a29eea9203e0062de7be56d1f775845c34456d9" + sha256: cf06fde8c002f326dc6cbf69ee3f97c3feead4436229da02d2e2aa39d5a5dbf4 url: "https://pub.dev" source: hosted - version: "2.0.0+1" + version: "2.1.0" flutter_custom_tabs_ios: dependency: transitive description: name: flutter_custom_tabs_ios - sha256: b29f687f7f366159d37347f888369fcd1259adac8ca6c7f07d356a80b091e08a + sha256: ef2de533bc45fb84fefc3854bc8b1e43001671c6bc6bc55faa57942eecd3f70a url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" flutter_custom_tabs_platform_interface: dependency: transitive description: name: flutter_custom_tabs_platform_interface - sha256: "13531a743486695d87b462b151801e11476b1b5a15274caec092420cc1943064" + sha256: e18e9b08f92582123bdb84fb6e4c91804b0579700fed6f887d32fd9a1e96a5d5 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" flutter_custom_tabs_web: dependency: transitive description: name: flutter_custom_tabs_web - sha256: "3114b511d3942ed42c502cc57ad47eaef9622ac06dd036e2b21b19d8876f6498" + sha256: "08ae322b11e1972a233d057542279873d0f9d1d5f8159c2c741457239d9d562c" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" flutter_driver: dependency: "direct dev" description: flutter @@ -619,10 +603,10 @@ packages: dependency: transitive description: name: flutter_image_compress_ohos - sha256: "70360371698be994786e5dd2e364a6525b1c5a4f843bff8af9b8a2fbe808d8d8" + sha256: e76b92bbc830ee08f5b05962fc78a532011fcd2041f620b5400a593e96da3f51 url: "https://pub.dev" source: hosted - version: "0.0.2" + version: "0.0.3" flutter_image_compress_platform_interface: dependency: transitive description: @@ -688,50 +672,50 @@ packages: dependency: "direct main" description: name: flutter_secure_storage - sha256: ffdbb60130e4665d2af814a0267c481bcf522c41ae2e43caf69fa0146876d685 + sha256: "8496a89eea74e23f92581885f876455d9d460e71201405dffe5f55dfe1155864" url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "9.2.1" flutter_secure_storage_linux: dependency: transitive description: name: flutter_secure_storage_linux - sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e" + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" flutter_secure_storage_macos: dependency: transitive description: name: flutter_secure_storage_macos - sha256: bd33935b4b628abd0b86c8ca20655c5b36275c3a3f5194769a7b3f37c905369c + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.2" flutter_secure_storage_platform_interface: dependency: transitive description: name: flutter_secure_storage_platform_interface - sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e" + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.2" flutter_secure_storage_web: dependency: transitive description: name: flutter_secure_storage_web - sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20" + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.1" flutter_secure_storage_windows: dependency: transitive description: name: flutter_secure_storage_windows - sha256: "5809c66f9dd3b4b93b0a6e2e8561539405322ee767ac2f64d084e2ab5429d108" + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.1.2" flutter_svg: dependency: "direct main" description: @@ -758,14 +742,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.5+3" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d - url: "https://pub.dev" - source: hosted - version: "2.4.1" fuchsia_remote_debug_protocol: dependency: transitive description: flutter @@ -844,7 +820,7 @@ packages: source: hosted version: "1.2.0" http_parser: - dependency: transitive + dependency: "direct main" description: name: http_parser sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" @@ -871,10 +847,10 @@ packages: dependency: transitive description: name: image_picker_android - sha256: "8e75431a62b7feb4fd55cb4a5c6f0ac4564460ec5dc09f9c4a0d50a5ce7c4cb9" + sha256: "0f57fee1e8bfadf8cc41818bbcd7f72e53bb768a54d9496355d5e8a5681a19f1" url: "https://pub.dev" source: hosted - version: "0.8.10" + version: "0.8.12+1" image_picker_for_web: dependency: transitive description: @@ -887,10 +863,10 @@ packages: dependency: transitive description: name: image_picker_ios - sha256: f4a6f62be96d6fd268f32a6bf8ef444cd8e3fff64d16923c6e6fe55e0c84a761 + sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" url: "https://pub.dev" source: hosted - version: "0.8.10" + version: "0.8.12" image_picker_linux: dependency: transitive description: @@ -927,10 +903,10 @@ packages: dependency: transitive description: name: in_app_review - sha256: "6cb7a8e4a2eecfa5868b35e1e9ac9082341eeead2cefaac8282be514736e9715" + sha256: "99869244d09adc76af16bf8fd731dd13cef58ecafd5917847589c49f378cbb30" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.0.9" in_app_review_platform_interface: dependency: transitive description: @@ -948,10 +924,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" iso_countries: dependency: "direct main" description: @@ -972,10 +948,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" latlong2: dependency: "direct main" description: @@ -988,26 +964,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -1028,10 +1004,10 @@ packages: dependency: transitive description: name: logger - sha256: "8c94b8c219e7e50194efc8771cd0e9f10807d8d3e219af473d89b06cc2ee4e04" + sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.0" logging: dependency: transitive description: @@ -1076,10 +1052,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mgrs_dart: dependency: transitive description: @@ -1100,10 +1076,10 @@ packages: dependency: transitive description: name: mobile_scanner - sha256: cf978740676ba5b0c17399baf117984b31190bb7a6eaa43e51229ed46abc42ee + sha256: "827765afbd4792ff3fd105ad593821ac0f6d8a7d352689013b07ee85be336312" url: "https://pub.dev" source: hosted - version: "3.5.2" + version: "4.0.1" mockito: dependency: "direct dev" description: @@ -1132,10 +1108,10 @@ packages: dependency: "direct main" description: name: openfoodfacts - sha256: "1b9954e093ead9c314e21e25a92e89540adae2212ae0535f6e69804b312ff972" + sha256: "90a853e7536d0f97a665b18cd602055215520bf424765ef06930e4be25b760ba" url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "3.11.0" openfoodfacts_flutter_lints: dependency: "direct dev" description: @@ -1205,10 +1181,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -1233,14 +1209,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" - percent_indicator: - dependency: "direct main" - description: - name: percent_indicator - sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c - url: "https://pub.dev" - source: hosted - version: "4.2.3" permission_handler: dependency: "direct main" description: @@ -1253,18 +1221,18 @@ packages: dependency: transitive description: name: permission_handler_android - sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" + sha256: b29a799ca03be9f999aa6c39f7de5209482d638e6f857f6b93b0875c618b7e54 url: "https://pub.dev" source: hosted - version: "12.0.5" + version: "12.0.7" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662 + sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 url: "https://pub.dev" source: hosted - version: "9.4.4" + version: "9.4.5" permission_handler_html: dependency: transitive description: @@ -1321,14 +1289,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - pointycastle: - dependency: transitive - description: - name: pointycastle - sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744" - url: "https://pub.dev" - source: hosted - version: "3.9.0" polylabel: dependency: transitive description: @@ -1389,18 +1349,18 @@ packages: dependency: "direct main" description: name: rive - sha256: "95690a0fb4f6e195c53b217ab3cc0e0b0f443c670adbdee9d57d636a36b82b18" + sha256: "255ab7892a77494458846cecee1376a017e64fd6b4130b51ec21424d12ffa6fe" url: "https://pub.dev" source: hosted - version: "0.13.2" + version: "0.13.4" rive_common: dependency: transitive description: name: rive_common - sha256: "3eee68fcab3e0882090cea5a8cf7acea7967f469a34a2580322575603b094435" + sha256: "3a0d95f529d52caef535d8ff32d75629ca37f7ab4707b13c83e9552a322557bc" url: "https://pub.dev" source: hosted - version: "0.4.5" + version: "0.4.8" scanner_ml_kit: dependency: "direct main" description: @@ -1422,22 +1382,6 @@ packages: relative: true source: path version: "1.0.0" - sensors_plus: - dependency: "direct main" - description: - name: sensors_plus - sha256: a1e461f28a8e8d3f81feb07d5c4e87e948379ea91f0b5131266bb79f72b38acb - url: "https://pub.dev" - source: hosted - version: "3.1.0" - sensors_plus_platform_interface: - dependency: transitive - description: - name: sensors_plus_platform_interface - sha256: bc472d6cfd622acb4f020e726433ee31788b038056691ba433fec80e448a094f - url: "https://pub.dev" - source: hosted - version: "1.2.0" sentry: dependency: transitive description: @@ -1490,10 +1434,10 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.4.0" shared_preferences_linux: dependency: transitive description: @@ -1539,6 +1483,14 @@ packages: description: flutter source: sdk version: "0.0.99" + sliver_tools: + dependency: "direct main" + description: + name: sliver_tools + sha256: eae28220badfb9d0559207badcbbc9ad5331aac829a88cb0964d330d2a4636a6 + url: "https://pub.dev" + source: hosted + version: "0.2.12" source_gen: dependency: transitive description: @@ -1579,6 +1531,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.4" + sqflite_common_ffi: + dependency: "direct main" + description: + name: sqflite_common_ffi + sha256: "4d6137c29e930d6e4a8ff373989dd9de7bac12e3bc87bce950f6e844e8ad3bb5" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: "1abbeb84bf2b1a10e5e1138c913123c8aa9d83cd64e5f9a0dd847b3c83063202" + url: "https://pub.dev" + source: hosted + version: "2.4.2" stack_trace: dependency: transitive description: @@ -1639,10 +1607,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -1679,18 +1647,18 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775" + sha256: "17cd5e205ea615e2c6ea7a77323a11712dffa0720a8a90540db57a01347f9ad9" url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.3.2" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" + sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89" url: "https://pub.dev" source: hosted - version: "6.2.5" + version: "6.3.0" url_launcher_linux: dependency: transitive description: @@ -1703,10 +1671,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.2.0" url_launcher_platform_interface: dependency: transitive description: @@ -1783,10 +1751,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" watcher: dependency: transitive description: @@ -1823,10 +1791,10 @@ packages: dependency: "direct main" description: name: webview_flutter_android - sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934 + sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc url: "https://pub.dev" source: hosted - version: "3.16.0" + version: "3.16.4" webview_flutter_platform_interface: dependency: transitive description: @@ -1847,10 +1815,10 @@ packages: dependency: transitive description: name: win32 - sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a" + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.5.0" win32_registry: dependency: transitive description: @@ -1892,5 +1860,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.3 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.19.0" diff --git a/packages/smooth_app/pubspec.yaml b/packages/smooth_app/pubspec.yaml index 0f28ed2673a..c1e4771e06f 100644 --- a/packages/smooth_app/pubspec.yaml +++ b/packages/smooth_app/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.0+734 publish_to: "none" environment: - sdk: '>=3.3.3 <4.0.0' + sdk: '>=3.4.0 <4.0.0' dependencies: flutter: @@ -17,16 +17,16 @@ dependencies: barcode_widget: 2.0.4 carousel_slider: 4.2.1 cupertino_icons: 1.0.8 - device_preview: 1.1.0 flutter_svg: 2.0.10+1 flutter_map: 6.1.0 html: 0.15.4 flutter_widget_from_html_core: 0.8.5+3 fwfh_selectable_text: 0.8.3+1 - flutter_secure_storage: 9.0.0 + flutter_secure_storage: 9.2.1 hive: 2.2.3 hive_flutter: 1.1.0 http: 1.2.0 + http_parser: 4.0.2 image_picker: 1.1.1 iso_countries: 2.2.0 latlong2: 0.9.1 @@ -39,6 +39,7 @@ dependencies: provider: 6.1.2 sentry_flutter: 7.18.0 sqflite: 2.3.3+1 + sqflite_common_ffi: 2.3.3 url_launcher: 6.1.3 visibility_detector: 0.4.0+2 assorted_layout_widgets: 9.0.1 @@ -47,32 +48,31 @@ dependencies: app_store_shared: path: ../app_store/shared audioplayers: 5.2.1 - percent_indicator: 4.2.3 flutter_email_sender: 6.0.3 flutter_native_splash: 2.4.0 image: 4.1.7 auto_size_text: 3.0.0 - crop_image: 1.0.12 + crop_image: 1.0.13 shared_preferences: 2.2.3 - intl: 0.18.1 + intl: 0.19.0 collection: 1.18.0 path: 1.9.0 path_provider: 2.1.3 share_plus: 7.2.2 fimber: 0.7.0 shimmer: ^3.0.0 - rive: 0.13.2 - sensors_plus: 3.1.0 + rive: 0.13.4 webview_flutter: 4.7.0 - webview_flutter_android: 3.16.0 + webview_flutter_android: 3.16.4 webview_flutter_wkwebview: 3.13.0 flutter_custom_tabs: 2.0.0+1 flutter_image_compress: 2.2.0 - connectivity_plus: 6.0.3 + connectivity_plus: 5.0.2 dart_ping: 9.0.1 dart_ping_ios: 4.0.2 flutter_animation_progress_bar: 2.3.1 email_validator: 2.1.17 + sliver_tools: 0.2.12 # According to the build variant, only one "app store" implementation must be added when building a release # Call "flutter pub remove xxxx" to remove unused dependencies @@ -88,7 +88,7 @@ dependencies: # We use two different scanning engines, # mobile scanner powered by ML Kit for the Play Store and Apple App Store, # but qr_code_scanner which uses the open source ZXing for F-Droid - camera: 0.10.5+9 + camera: 0.10.6 scanner_shared: path: ../scanner/shared @@ -100,7 +100,7 @@ dependencies: path: ../scanner/zxing - openfoodfacts: 3.6.1 + openfoodfacts: 3.11.0 # openfoodfacts: # path: ../../../openfoodfacts-dart diff --git a/packages/smooth_app/test/cache/cache_test.dart b/packages/smooth_app/test/cache/cache_test.dart deleted file mode 100644 index 3ef119aac6e..00000000000 --- a/packages/smooth_app/test/cache/cache_test.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:typed_data'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; -import 'package:smooth_app/cache/files/file_cache_manager.dart'; - -import '../tests_utils/path_provider_mock.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - PathProviderPlatform.instance = MockedPathProviderPlatform(); - - _runTests(FileCacheManager.temporary, 'Temporary'); - _runTests(FileCacheManager.persistent, 'Persistent'); -} - -void _runTests(FileCache cache, String debugLabel) { - const String testFileContent = 'Hello World'; - const String fileKey = 'hello'; - final String label = '[$debugLabel]'; - - test('$label Add file', () async { - final FileCache cache = FileCacheManager.temporary; - expect( - await cache.put(fileKey, Uint8List.fromList(testFileContent.codeUnits)), - true); - expect(await cache.containsKey(fileKey), true); - }); - test('$label Get file', () async { - expect( - await cache.put(fileKey, Uint8List.fromList(testFileContent.codeUnits)), - true, - ); - final Uint8List? content = await cache.get(fileKey); - - expect( - String.fromCharCodes( - content!.toList(growable: false), - ), - testFileContent); - }); - - test('$label Remove file', () async { - expect( - await cache.put(fileKey, Uint8List.fromList(testFileContent.codeUnits)), - true); - expect(await cache.remove(fileKey), true); - expect(await cache.containsKey(fileKey), false); - }); - - test('$label Clear cache', () async { - await cache.clear(); - expect(await cache.length, 0); - }); -} diff --git a/version.txt b/version.txt index c412a4e2e1e..5c517bf11d5 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -4.14.0 +4.15.0