diff --git a/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/2.png b/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/2.png index da3fea3..8c95790 100644 Binary files a/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/2.png and b/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/2.png differ diff --git a/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/3.png b/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/3.png index dd12bce..de0c402 100644 Binary files a/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/3.png and b/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/3.png differ diff --git a/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/4.png b/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/4.png index ca24654..f87fa3e 100644 Binary files a/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/4.png and b/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/4.png differ diff --git a/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/5.png b/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/5.png index adb4f7a..f9e9a92 100644 Binary files a/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/5.png and b/apps/app_seeds/fastlane/metadata/android/es-ES/images/phoneScreenshots/5.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/de/calendar.png b/apps/app_seeds/test/screenshots/goldens/de/calendar.png index bd9c6b3..4927225 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/de/calendar.png and b/apps/app_seeds/test/screenshots/goldens/de/calendar.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/de/detail.png b/apps/app_seeds/test/screenshots/goldens/de/detail.png index 4472e82..abdaffd 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/de/detail.png and b/apps/app_seeds/test/screenshots/goldens/de/detail.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/de/inventory.png b/apps/app_seeds/test/screenshots/goldens/de/inventory.png index fae1838..c3b27af 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/de/inventory.png and b/apps/app_seeds/test/screenshots/goldens/de/inventory.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/de/market.png b/apps/app_seeds/test/screenshots/goldens/de/market.png index b6413ef..80649b3 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/de/market.png and b/apps/app_seeds/test/screenshots/goldens/de/market.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/es/calendar.png b/apps/app_seeds/test/screenshots/goldens/es/calendar.png index ca24654..f87fa3e 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/es/calendar.png and b/apps/app_seeds/test/screenshots/goldens/es/calendar.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/es/detail.png b/apps/app_seeds/test/screenshots/goldens/es/detail.png index adb4f7a..f9e9a92 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/es/detail.png and b/apps/app_seeds/test/screenshots/goldens/es/detail.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/es/inventory.png b/apps/app_seeds/test/screenshots/goldens/es/inventory.png index da3fea3..8c95790 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/es/inventory.png and b/apps/app_seeds/test/screenshots/goldens/es/inventory.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/es/market.png b/apps/app_seeds/test/screenshots/goldens/es/market.png index dd12bce..de0c402 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/es/market.png and b/apps/app_seeds/test/screenshots/goldens/es/market.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/fr/calendar.png b/apps/app_seeds/test/screenshots/goldens/fr/calendar.png index 063407a..23de5f8 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/fr/calendar.png and b/apps/app_seeds/test/screenshots/goldens/fr/calendar.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/fr/detail.png b/apps/app_seeds/test/screenshots/goldens/fr/detail.png index 92e12ac..85281ec 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/fr/detail.png and b/apps/app_seeds/test/screenshots/goldens/fr/detail.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/fr/inventory.png b/apps/app_seeds/test/screenshots/goldens/fr/inventory.png index d01faaf..64a91c1 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/fr/inventory.png and b/apps/app_seeds/test/screenshots/goldens/fr/inventory.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/fr/market.png b/apps/app_seeds/test/screenshots/goldens/fr/market.png index 346b39d..82906d8 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/fr/market.png and b/apps/app_seeds/test/screenshots/goldens/fr/market.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/pt/calendar.png b/apps/app_seeds/test/screenshots/goldens/pt/calendar.png index c556c1d..646ae1f 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/pt/calendar.png and b/apps/app_seeds/test/screenshots/goldens/pt/calendar.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/pt/detail.png b/apps/app_seeds/test/screenshots/goldens/pt/detail.png index 9e2bf2f..33d22e8 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/pt/detail.png and b/apps/app_seeds/test/screenshots/goldens/pt/detail.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/pt/inventory.png b/apps/app_seeds/test/screenshots/goldens/pt/inventory.png index c271504..3469de0 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/pt/inventory.png and b/apps/app_seeds/test/screenshots/goldens/pt/inventory.png differ diff --git a/apps/app_seeds/test/screenshots/goldens/pt/market.png b/apps/app_seeds/test/screenshots/goldens/pt/market.png index 9e6b437..bf4ed59 100644 Binary files a/apps/app_seeds/test/screenshots/goldens/pt/market.png and b/apps/app_seeds/test/screenshots/goldens/pt/market.png differ diff --git a/apps/app_seeds/test/screenshots/screenshot_support.dart b/apps/app_seeds/test/screenshots/screenshot_support.dart index 37aaaea..878ab53 100644 --- a/apps/app_seeds/test/screenshots/screenshot_support.dart +++ b/apps/app_seeds/test/screenshots/screenshot_support.dart @@ -71,6 +71,31 @@ Future loadScreenshotFonts() async { _fontsLoaded = true; } +/// Localized sample labels so each screenshot reads in its own language. Family +/// names stay botanical Latin; only the human variety/offer names are localized. +/// English is the fallback (e.g. for `ja`, whose UI is itself partly English). +const sampleLabels = >{ + // Inventory varieties + 'maize': {AppLocale.en: 'Painted flint corn', AppLocale.es: 'Maíz rojo', AppLocale.fr: 'Maïs corné rouge', AppLocale.de: 'Roter Hartmais', AppLocale.pt: 'Milho pintado'}, + 'buckwheat': {AppLocale.en: 'Common buckwheat', AppLocale.es: 'Trigo sarraceno', AppLocale.fr: 'Sarrasin', AppLocale.de: 'Buchweizen', AppLocale.pt: 'Trigo-mourisco'}, + 'raspberry': {AppLocale.en: 'Wild raspberry', AppLocale.es: 'Frambuesa', AppLocale.fr: 'Framboise sauvage', AppLocale.de: 'Wilde Himbeere', AppLocale.pt: 'Framboesa'}, + 'tomato': {AppLocale.en: 'Slicing tomato', AppLocale.es: 'Tomate de ensalada', AppLocale.fr: 'Tomate à trancher', AppLocale.de: 'Fleischtomate', AppLocale.pt: 'Tomate para salada'}, + 'chilli': {AppLocale.en: 'Guajillo chilli', AppLocale.es: 'Chile guajillo', AppLocale.fr: 'Piment guajillo', AppLocale.de: 'Guajillo-Chili', AppLocale.pt: 'Pimenta guajillo'}, + 'origin': {AppLocale.en: 'Community seed swap', AppLocale.es: 'Intercambio de semillas', AppLocale.fr: 'Troc de graines', AppLocale.de: 'Saatgut-Tausch', AppLocale.pt: 'Troca de sementes'}, + // Market offers + 'cherryTomato': {AppLocale.en: 'Cherry tomato', AppLocale.es: 'Tomate cherry', AppLocale.fr: 'Tomate cerise', AppLocale.de: 'Kirschtomate', AppLocale.pt: 'Tomate-cereja'}, + 'bean': {AppLocale.en: 'Climbing bean', AppLocale.es: 'Judía de enrame', AppLocale.fr: 'Haricot à rames', AppLocale.de: 'Stangenbohne', AppLocale.pt: 'Feijão-trepador'}, + 'sunflower': {AppLocale.en: 'Sunflower', AppLocale.es: 'Girasol', AppLocale.fr: 'Tournesol', AppLocale.de: 'Sonnenblume', AppLocale.pt: 'Girassol'}, + 'basil': {AppLocale.en: 'Sweet basil', AppLocale.es: 'Albahaca', AppLocale.fr: 'Basilic', AppLocale.de: 'Basilikum', AppLocale.pt: 'Manjericão'}, + 'carrot': {AppLocale.en: 'Purple carrot', AppLocale.es: 'Zanahoria morada', AppLocale.fr: 'Carotte violette', AppLocale.de: 'Violette Karotte', AppLocale.pt: 'Cenoura roxa'}, +}; + +/// The sample label for [key] in [locale], falling back to English. +String labelFor(String key, AppLocale locale) { + final m = sampleLabels[key]!; + return m[locale] ?? m[AppLocale.en]!; +} + const _fontFallbacks = ['Noto Sans JP', 'Noto Sans Arabic']; /// The real app theme, but with every text style pinned to the bundled @@ -146,7 +171,9 @@ class SeededInventory { Future seedShowcase( AppDatabase db, VarietyRepository repo, + AppLocale locale, ) async { + String l(String key) => labelFor(key, locale); // Sow in spring, harvest seed in late summer — visible in the calendar. final sow = monthsToMask(const [3, 4, 5]); final harvest = monthsToMask(const [8, 9]); @@ -174,16 +201,12 @@ Future seedShowcase( Future photo(String name) => File('test/screenshots/fixtures/$name.jpg').readAsBytes(); - final maize = await add( - 'Painted flint corn', - 'Poaceae', - photo: await photo('maize'), - ); + final maize = await add(l('maize'), 'Poaceae', photo: await photo('maize')); await repo.addLot( varietyId: maize, harvestYear: 2024, quantity: const Quantity(kind: QuantityKind.cob, count: 6), - originName: 'Community seed swap', + originName: l('origin'), abundance: Abundance.plentyToShare, offerStatus: OfferStatus.shared, ); @@ -196,10 +219,10 @@ Future seedShowcase( // The rest sit in families that sort at/after Poaceae, so the maize stays the // first, top-of-list group in the inventory shot. Each carries a CC0/PD photo. - await add('Common buckwheat', 'Polygonaceae', photo: await photo('buckwheat')); - await add('Wild raspberry', 'Rosaceae', photo: await photo('raspberry')); - await add('Slicing tomato', 'Solanaceae', photo: await photo('tomato')); - await add('Guajillo chilli', 'Solanaceae', photo: await photo('pepper')); + await add(l('buckwheat'), 'Polygonaceae', photo: await photo('buckwheat')); + await add(l('raspberry'), 'Rosaceae', photo: await photo('raspberry')); + await add(l('tomato'), 'Solanaceae', photo: await photo('tomato')); + await add(l('chilli'), 'Solanaceae', photo: await photo('pepper')); return SeededInventory(maize); } diff --git a/apps/app_seeds/test/screenshots/screenshots_test.dart b/apps/app_seeds/test/screenshots/screenshots_test.dart index 35ad0f2..fac0139 100644 --- a/apps/app_seeds/test/screenshots/screenshots_test.dart +++ b/apps/app_seeds/test/screenshots/screenshots_test.dart @@ -45,13 +45,11 @@ void main() { late AppDatabase db; late VarietyRepository repo; late SpeciesRepository species; - late SeededInventory seeded; setUp(() async { db = newTestDatabase(); repo = newTestRepository(db); species = newTestSpeciesRepository(db); - seeded = await seedShowcase(db, repo); }); tearDown(() => db.close()); @@ -79,45 +77,52 @@ void main() { // A POPULATED market: a seeded OffersCubit (no transport) feeding MarketBody, // so the offer list renders with no live discovery stream to hang - // pumpAndSettle. (Live discovery is exercised by offers_cubit_test.dart; see - // the note in market_screen_test.dart on why we don't drive it via widgets.) - Future market() async => Builder( + // pumpAndSettle. Offer names are localized. (Live discovery is exercised by + // offers_cubit_test.dart; see market_screen_test.dart on why not via widgets.) + Widget marketWidget(AppLocale locale) => Builder( builder: (context) => Scaffold( appBar: AppBar( title: Text(context.t.market.title), actions: [IconButton(icon: const Icon(Icons.tune), onPressed: () {})], ), body: BlocProvider( - create: (_) => _SeededOffersCubit(_sampleOffers()), + create: (_) => _SeededOffersCubit(_sampleOffers(locale)), child: MarketBody(onConfigure: () {}), ), ), ); - /// Each entry renders one screen; the runner re-seeds and re-locales per shot. - final screens = Function()>{ - 'home': () async => const HomeScreen(marketEnabled: true), - 'inventory': () async => const InventoryListScreen(), - 'market': market, - 'calendar': () async => const CalendarScreen(initialMonth: 4), - 'detail': () async => BlocProvider( - create: (_) => VarietyDetailCubit(repo, seeded.heroVarietyId), - child: const VarietyDetailScreen(), - ), - }; + const screenNames = ['home', 'inventory', 'market', 'calendar', 'detail']; for (final locale in screenshotLocales) { - for (final entry in screens.entries) { - testWidgets('${locale.languageCode}/${entry.key}', (tester) async { + for (final name in screenNames) { + testWidgets('${locale.languageCode}/$name', (tester) async { + // Re-seed per shot so the sample variety names are in the shot's + // language. Drift writes must run in REAL async (runAsync), not the + // testWidgets fake clock, or they hang. + late final SeededInventory seeded; + await tester.runAsync(() async { + seeded = await seedShowcase(db, repo, locale); + }); + final child = switch (name) { + 'home' => const HomeScreen(marketEnabled: true), + 'inventory' => const InventoryListScreen(), + 'market' => marketWidget(locale), + 'calendar' => const CalendarScreen(initialMonth: 4), + _ => BlocProvider( + create: (_) => VarietyDetailCubit(repo, seeded.heroVarietyId), + child: const VarietyDetailScreen(), + ), + }; await tester.pumpWidget( screenshotApp( repository: repo, species: species, locale: locale, - child: await entry.value(), + child: child, ), ); - await capture(tester, locale.languageCode, entry.key); + await capture(tester, locale.languageCode, name); }); } } @@ -125,6 +130,7 @@ void main() { // RTL layout demo: base (English) strings, forced right-to-left, so the // mirrored inventory proves the design is RTL-safe. Not a translated locale. testWidgets('rtl/inventory', (tester) async { + await tester.runAsync(() => seedShowcase(db, repo, AppLocale.en)); await tester.pumpWidget( screenshotApp( repository: repo, @@ -159,12 +165,12 @@ class _NoopOfferTransport implements OfferTransport { Future close() async {} } -/// A small, internationally-neutral set of nearby offers across gift / swap / -/// sale and colour-coded botanical families. -List _sampleOffers() => [ - Offer(id: 'o1', authorPubkeyHex: 'a1' * 32, summary: 'Cherry tomato', type: OfferType.gift, category: 'Solanaceae', approxGeohash: 'sp3e9', isOrganic: true), - Offer(id: 'o2', authorPubkeyHex: 'a2' * 32, summary: 'Climbing bean', type: OfferType.exchange, category: 'Fabaceae', approxGeohash: 'sp3e8'), - Offer(id: 'o3', authorPubkeyHex: 'a3' * 32, summary: 'Sunflower', type: OfferType.sale, category: 'Asteraceae', approxGeohash: 'sp3ec', priceAmount: 2, priceCurrency: '€'), - Offer(id: 'o4', authorPubkeyHex: 'a4' * 32, summary: 'Sweet basil', type: OfferType.gift, category: 'Lamiaceae', approxGeohash: 'sp3e2', isOrganic: true), - Offer(id: 'o5', authorPubkeyHex: 'a5' * 32, summary: 'Purple carrot', type: OfferType.exchange, category: 'Apiaceae', approxGeohash: 'sp3ef'), +/// A small set of nearby offers across gift / swap / sale and colour-coded +/// botanical families, with names localized to [l]. +List _sampleOffers(AppLocale l) => [ + Offer(id: 'o1', authorPubkeyHex: 'a1' * 32, summary: labelFor('cherryTomato', l), type: OfferType.gift, category: 'Solanaceae', approxGeohash: 'sp3e9', isOrganic: true), + Offer(id: 'o2', authorPubkeyHex: 'a2' * 32, summary: labelFor('bean', l), type: OfferType.exchange, category: 'Fabaceae', approxGeohash: 'sp3e8'), + Offer(id: 'o3', authorPubkeyHex: 'a3' * 32, summary: labelFor('sunflower', l), type: OfferType.sale, category: 'Asteraceae', approxGeohash: 'sp3ec', priceAmount: 2, priceCurrency: '€'), + Offer(id: 'o4', authorPubkeyHex: 'a4' * 32, summary: labelFor('basil', l), type: OfferType.gift, category: 'Lamiaceae', approxGeohash: 'sp3e2', isOrganic: true), + Offer(id: 'o5', authorPubkeyHex: 'a5' * 32, summary: labelFor('carrot', l), type: OfferType.exchange, category: 'Apiaceae', approxGeohash: 'sp3ef'), ]; diff --git a/site/assets/screenshots/de/calendar.png b/site/assets/screenshots/de/calendar.png index bd9c6b3..4927225 100644 Binary files a/site/assets/screenshots/de/calendar.png and b/site/assets/screenshots/de/calendar.png differ diff --git a/site/assets/screenshots/de/detail.png b/site/assets/screenshots/de/detail.png index 4472e82..abdaffd 100644 Binary files a/site/assets/screenshots/de/detail.png and b/site/assets/screenshots/de/detail.png differ diff --git a/site/assets/screenshots/de/inventory.png b/site/assets/screenshots/de/inventory.png index fae1838..c3b27af 100644 Binary files a/site/assets/screenshots/de/inventory.png and b/site/assets/screenshots/de/inventory.png differ diff --git a/site/assets/screenshots/de/market.png b/site/assets/screenshots/de/market.png index b6413ef..80649b3 100644 Binary files a/site/assets/screenshots/de/market.png and b/site/assets/screenshots/de/market.png differ diff --git a/site/assets/screenshots/es/calendar.png b/site/assets/screenshots/es/calendar.png index ca24654..f87fa3e 100644 Binary files a/site/assets/screenshots/es/calendar.png and b/site/assets/screenshots/es/calendar.png differ diff --git a/site/assets/screenshots/es/detail.png b/site/assets/screenshots/es/detail.png index adb4f7a..f9e9a92 100644 Binary files a/site/assets/screenshots/es/detail.png and b/site/assets/screenshots/es/detail.png differ diff --git a/site/assets/screenshots/es/inventory.png b/site/assets/screenshots/es/inventory.png index da3fea3..8c95790 100644 Binary files a/site/assets/screenshots/es/inventory.png and b/site/assets/screenshots/es/inventory.png differ diff --git a/site/assets/screenshots/es/market.png b/site/assets/screenshots/es/market.png index dd12bce..de0c402 100644 Binary files a/site/assets/screenshots/es/market.png and b/site/assets/screenshots/es/market.png differ diff --git a/site/assets/screenshots/fr/calendar.png b/site/assets/screenshots/fr/calendar.png index 063407a..23de5f8 100644 Binary files a/site/assets/screenshots/fr/calendar.png and b/site/assets/screenshots/fr/calendar.png differ diff --git a/site/assets/screenshots/fr/detail.png b/site/assets/screenshots/fr/detail.png index 92e12ac..85281ec 100644 Binary files a/site/assets/screenshots/fr/detail.png and b/site/assets/screenshots/fr/detail.png differ diff --git a/site/assets/screenshots/fr/inventory.png b/site/assets/screenshots/fr/inventory.png index d01faaf..64a91c1 100644 Binary files a/site/assets/screenshots/fr/inventory.png and b/site/assets/screenshots/fr/inventory.png differ diff --git a/site/assets/screenshots/fr/market.png b/site/assets/screenshots/fr/market.png index 346b39d..82906d8 100644 Binary files a/site/assets/screenshots/fr/market.png and b/site/assets/screenshots/fr/market.png differ diff --git a/site/assets/screenshots/pt/calendar.png b/site/assets/screenshots/pt/calendar.png index c556c1d..646ae1f 100644 Binary files a/site/assets/screenshots/pt/calendar.png and b/site/assets/screenshots/pt/calendar.png differ diff --git a/site/assets/screenshots/pt/detail.png b/site/assets/screenshots/pt/detail.png index 9e2bf2f..33d22e8 100644 Binary files a/site/assets/screenshots/pt/detail.png and b/site/assets/screenshots/pt/detail.png differ diff --git a/site/assets/screenshots/pt/inventory.png b/site/assets/screenshots/pt/inventory.png index c271504..3469de0 100644 Binary files a/site/assets/screenshots/pt/inventory.png and b/site/assets/screenshots/pt/inventory.png differ diff --git a/site/assets/screenshots/pt/market.png b/site/assets/screenshots/pt/market.png index 9e6b437..bf4ed59 100644 Binary files a/site/assets/screenshots/pt/market.png and b/site/assets/screenshots/pt/market.png differ diff --git a/site/static/og-es.png b/site/static/og-es.png index 9533e75..ff6c3cc 100644 Binary files a/site/static/og-es.png and b/site/static/og-es.png differ