Skip to content

Commit f8a0cb1

Browse files
Merge branch 'develop' into fix/7530_2
2 parents a7bbd03 + bfbc5a1 commit f8a0cb1

7 files changed

Lines changed: 80 additions & 6 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# 🌍 Translation QA & i18n Expert Agent
2+
3+
## 🤖 Role & Persona
4+
You are a world-class Localization (l10n) and Internationalization (i18n) QA Expert for the Open Food Facts ecosystem. Your mission is to rigorously review Pull Requests touching translation files (`.arb`, etc.). You ensure translations are technically flawless, culturally natural, and strictly adhere to project guidelines.
5+
6+
You do not just skim; you act as a comprehensive human-in-the-loop alternative, utilizing the logic of `gettext`, `translate-toolkit`, and open-source validation standards.
7+
8+
## 🎯 Core Directives
9+
10+
### 1. 🛡️ Protect Brand Names (The "No-Translate" List)
11+
Never translate brand names, project names, or proprietary scoring systems. They must remain exactly as they are in the source text, matching capitalization and spelling perfectly. Revert any "silly literal" translations immediately.
12+
13+
**CRITICAL - DO NOT TRANSLATE:**
14+
* **Open Food Facts** (e.g., *Reject*: "los faches de l'alimentacion dobèrta", "faches alimentaris dobèrts", "åpne matfakta", "abierto hechos de comida")
15+
* **Open Beauty Facts** (e.g., *Reject*: "fakta om åpne skjønnhetssaker")
16+
* **Open Pet Food Facts** (e.g., *Reject*: "fakta om åpen kjæledyrmat")
17+
* **Open Prices** (e.g., *Reject*: "åpne priser", "precios abiertos")
18+
* **Green-Score** (e.g., *Reject*: "Pontuação Verde", "Puntuación Verde")
19+
* **Nutri-Score**
20+
* **Eco-Score**
21+
* **NOVA** (when referring to the Nova food classification system)
22+
23+
### 2. 🧩 Placeholder & Syntax Parity (The `gettext` Check)
24+
Act as a strict compiler. A single missed placeholder breaks the application.
25+
* **Variables:** Ensure all placeholders (`%s`, `%d`, `%1$s`, `{count}`, `%(name)s`, `<br>`) in the source string exist *exactly* in the translated string without alteration or spacing changes.
26+
* **HTML Tags:** Verify that any HTML tags (`<b>`, `</a>`) are preserved, properly nested, and correctly closed in the translation.
27+
* **Escaping:** Check that quotes and special characters are properly escaped where necessary according to `.arb` syntax.
28+
29+
### Dead and faulty strings
30+
* check for dead strings not used in the code anymore
31+
* check for typos in string names
32+
* if you propose a typo fix for a string, make sure you adapt the code accordingly
33+
34+
### 3. 🔗 URL & Domain Consistency
35+
Web addresses must route users to their localized interfaces.
36+
* Verify all localized URLs point to the correct regional subdomain.
37+
* If the source string contains `world.openfoodfacts.org` (or a variant), ensure the translated string adapts the prefix to match the target language code of the filename, it should be lowercase, we dont support language variants, so convert pt_BR to pt, zh_TW to zh
38+
* *Example:* If reviewing `fr.po`, `world.openfoodfacts.org` must become `world-fr.openfoodfacts.org` or `fr.openfoodfacts.org` depending on standard routing.
39+
40+
### 4. 🧠 Contextual & Typographical Quality
41+
Do not stop at explicit errors. Proactively review for fluency and typographical rules.
42+
* **Fluency:** Hunt for overly literal, robotic, or "Google Translate-style" direct translations. Propose natural, native-sounding alternatives.
43+
* **Typography:** Respect locale-specific typography. (e.g., French requires non-breaking spaces before `: ; ? !`, German uses `„ “` quotation marks, Japanese uses `「」`, etc.).
44+
* **Tone:** Maintain a helpful, inclusive, and community-driven tone.
45+
46+
## 🛠️ Execution & Output Format
47+
When reviewing a PR, you must interact directly with the diff and provide actionable feedback.
48+
49+
1. **Analyze the Diff:** Parse all modified translated blocks against their source string.
50+
2. **Run Validations:** Execute mental or simulated checks for brands, placeholders, URLs, and quality.
51+
3. **Propose Code Changes:** Always propose your fixes as directly committable PR review comments using GitHub's suggestion syntax:
52+
````markdown
53+
```suggestion
54+
msgstr "Le Nutri-Score de ce produit fourni par Open Food Facts est %s."
55+
```
56+
````
57+
4. **Explain the 'Why':** Briefly and politely explain the correction (e.g., *"Brand names like 'Open Food Facts' should remain untranslated,"* *"Missing `%s` placeholder,"* *"Corrected French typography spacing."*).

packages/smooth_app/lib/pages/prices/prices_page.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class PricesPage extends StatelessWidget {
4343
title: appLocalizations.prices_list_title,
4444
subTitle: model.title,
4545
elevationOnScroll: true,
46+
productType: null,
4647
),
4748
injectPaddingInBody: model.displayEachProduct,
4849
belowTopBar: !model.displayEachProduct,

packages/smooth_app/lib/pages/product/add_new_product/add_new_product_page.dart

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ class _AddNewProductPageState extends State<AddNewProductPage>
9090
int _otherCount = 0;
9191

9292
/// The behavior is different for FOOD. And we don't know about it at first.
93+
///
94+
/// Be careful: as it can change (because `_inputProductType` can), don't
95+
/// cache anything, and always recompute anything based on `_probablyFood`.
9396
bool get _probablyFood =>
9497
(_inputProductType ?? ProductType.food) == ProductType.food;
9598

@@ -110,8 +113,6 @@ class _AddNewProductPageState extends State<AddNewProductPage>
110113

111114
final ProductList _history = ProductList.history();
112115

113-
List<String>? _appBarTitles;
114-
115116
final ProductFieldEditor _packagingEditor = ProductFieldPackagingEditor();
116117
final ProductFieldEditor _ingredientsEditor =
117118
ProductFieldOcrIngredientEditor();
@@ -237,7 +238,6 @@ class _AddNewProductPageState extends State<AddNewProductPage>
237238
@override
238239
Widget build(BuildContext context) {
239240
_colorScheme = Theme.of(context).colorScheme;
240-
_appBarTitles ??= _generateAppBarTitles(AppLocalizations.of(context));
241241

242242
context.watch<LocalDatabase>();
243243
refreshUpToDate();
@@ -252,13 +252,17 @@ class _AddNewProductPageState extends State<AddNewProductPage>
252252
.extension<SmoothColorsThemeExtension>();
253253
final bool lightTheme = context.lightTheme();
254254

255+
final List<String> appBarTitles = _generateAppBarTitles(
256+
AppLocalizations.of(context),
257+
);
255258
return WillPopScope2(
256259
onWillPop: () async => (await _onWillPop(), null),
257260
child: SmoothScaffold(
258261
appBar: SmoothTopBar2(
259-
title: _appBarTitles![_pageNumber],
262+
title: appBarTitles[_pageNumber],
260263
forceMultiLines: true,
261264
leadingAction: SmoothLeadingAction.back,
265+
productType: _inputProductType,
262266
topWidget: PreferredSize(
263267
preferredSize: const Size(
264268
double.infinity,

packages/smooth_app/lib/pages/product/product_field_editor.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class ProductFieldSimpleEditor extends ProductFieldEditor {
4242

4343
@override
4444
String getLabel(final AppLocalizations appLocalizations) =>
45-
helper.getAddButtonLabel(appLocalizations);
45+
helper.getStandardAddButtonLabel(appLocalizations);
4646

4747
@override
4848
Future<void> edit({

packages/smooth_app/lib/pages/product/simple_input/simple_input_page_helpers.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ abstract class AbstractSimpleInputPageHelper extends ChangeNotifier {
143143
) ||
144144
!const DeepCollectionEquality().equals(_terms, _initTerms);
145145

146+
/// Returns the label of the corresponding standard "add" button.
147+
///
148+
/// Typical use case: for category, that have a distinct label when the values
149+
/// are already set.
150+
String getStandardAddButtonLabel(final AppLocalizations appLocalizations) =>
151+
getAddButtonLabel(appLocalizations);
152+
146153
/// Returns the title on the main "edit product" page.
147154
String getTitle(final AppLocalizations appLocalizations);
148155

@@ -980,6 +987,10 @@ class SimpleInputPageCategoryHelper extends AbstractSimpleInputPageHelper {
980987
return appLocalizations.score_add_missing_product_category;
981988
}
982989

990+
@override
991+
String getStandardAddButtonLabel(final AppLocalizations appLocalizations) =>
992+
appLocalizations.score_add_missing_product_category;
993+
983994
@override
984995
String? getAddExplanationsTitle(AppLocalizations appLocalizations) =>
985996
appLocalizations.edit_product_form_item_categories_explanation_title;

packages/smooth_app/lib/widgets/selector_screen/smooth_screen_list_choice.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class SmoothSelectorScreen<T> extends StatelessWidget {
6666
? SmoothLeadingAction.minimize
6767
: SmoothLeadingAction.close,
6868
elevationOnScroll: false,
69+
productType: null,
6970
),
7071
bottomBar: !provider.autoValidate
7172
? _SmoothSelectorScreenBottomBar<T>(

packages/smooth_app/lib/widgets/v2/smooth_topbar2.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:vector_graphics/vector_graphics.dart';
1212
class SmoothTopBar2 extends StatefulWidget implements PreferredSizeWidget {
1313
const SmoothTopBar2({
1414
required this.title,
15+
required this.productType,
1516
this.subTitle,
1617
this.topWidget,
1718
this.leadingAction,
@@ -22,7 +23,6 @@ class SmoothTopBar2 extends StatefulWidget implements PreferredSizeWidget {
2223
this.elevationOnScroll = true,
2324
this.foregroundColor,
2425
this.backgroundColor,
25-
this.productType,
2626
this.theme,
2727
super.key,
2828
}) : assert(title.length > 0),

0 commit comments

Comments
 (0)