diff --git a/lib/ui2/screens/health_screen.dart b/lib/ui2/screens/health_screen.dart index 6221660a7..905680932 100644 --- a/lib/ui2/screens/health_screen.dart +++ b/lib/ui2/screens/health_screen.dart @@ -1026,8 +1026,11 @@ class _HealthScreenState extends State with RevisionReload { // Statistics off the STORED values; the painter gets the dense window. final s = valuesOf(pts); if (s.isEmpty) { + // Mid-sentence, like healthNoMetric: "No resting heart rate trend + // yet", not "No Resting…"; German keeps its noun case. + final noun = nounInSentence(l, label); return StatusCard( - l?.healthNoTrendYet(label) ?? 'No $label trend yet', + l?.healthNoTrendYet(noun) ?? 'No $noun trend yet', l?.healthZeroDaysStored ?? '0 days stored.', icon: LucideIcons.chartLine, ); diff --git a/lib/ui2/screens/investigate.dart b/lib/ui2/screens/investigate.dart index e77c4ac53..fa314c32c 100644 --- a/lib/ui2/screens/investigate.dart +++ b/lib/ui2/screens/investigate.dart @@ -1094,10 +1094,12 @@ class _InvestigateState extends State { return [ StatusCard( l?.investigateNoStoredSeries ?? 'No stored series', + // nounInSentence, not toLowerCase(): that read "hrv" in English + // and "Für ruhepuls" in German. l?.investigateNothingStoredYet( - uiText(context, spec.title).toLowerCase(), + nounInSentence(l, uiText(context, spec.title)), ) ?? - 'Nothing stored for ${uiText(context, spec.title).toLowerCase()} yet.', + 'Nothing stored for ${nounInSentence(l, uiText(context, spec.title))} yet.', icon: spec.icon, ), ]; diff --git a/test/investigate_empty_series_case_test.dart b/test/investigate_empty_series_case_test.dart new file mode 100644 index 000000000..5b7cb8fb7 --- /dev/null +++ b/test/investigate_empty_series_case_test.dart @@ -0,0 +1,46 @@ +// Investigate's "nothing stored yet" line put the metric name through a bare +// toLowerCase(), so English read "Nothing stored for rem sleep yet." and German +// "Für ruhepuls ist noch nichts gespeichert." It now goes through +// nounInSentence, the same rule the Health rows and the metric detail use. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:openstrap_edge/data/day_label.dart'; +import 'package:openstrap_edge/l10n/app_localizations.dart'; +import 'package:openstrap_edge/ui2/screens/screens.dart'; +import 'package:openstrap_edge/ui2/ui2.dart'; + +Future _pump(WidgetTester t, String key, {Locale? locale}) async { + t.view.physicalSize = const Size(390 * 3, 3000 * 3); + t.view.devicePixelRatio = 3; + addTearDown(t.view.reset); + await t.pumpWidget(MaterialApp( + theme: buildTheme(Brightness.light), + locale: locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: Investigate(key, + data: InvestigateData(day: todayLabel(), series: const [])), + )); + await t.pumpAndSettle(); +} + +void main() { + testWidgets('english lowercases a name but keeps an acronym', (t) async { + await _pump(t, 'resting_hr', locale: const Locale('en')); + expect(find.text('Nothing stored for resting heart rate yet.'), + findsOneWidget); + + await _pump(t, 'rem', locale: const Locale('en')); + expect(find.text('Nothing stored for REM sleep yet.'), findsOneWidget); + expect(find.text('Nothing stored for rem sleep yet.'), findsNothing); + }); + + testWidgets('german keeps the noun case', (t) async { + await _pump(t, 'resting_hr', locale: const Locale('de')); + expect(find.text('Für Ruhepuls ist noch nichts gespeichert.'), + findsOneWidget); + expect(find.text('Für ruhepuls ist noch nichts gespeichert.'), + findsNothing); + }); +} diff --git a/test/ui2_health_no_metric_case_test.dart b/test/ui2_health_no_metric_case_test.dart index 2f75a85a1..928e57bc3 100644 --- a/test/ui2_health_no_metric_case_test.dart +++ b/test/ui2_health_no_metric_case_test.dart @@ -58,4 +58,34 @@ void main() { await _pump(t, const Locale('en')); expect(find.text('No resting heart rate'), findsOneWidget); }); + + // Same rule on the Trends tab's empty card. It used to drop the label in + // as written, so English read "No Resting heart rate trend yet". + Future openTrends(WidgetTester t) async { + await t.tap(find.text('Trends')); + for (var i = 0; i < 20; i++) { + await t.pump(); + } + } + + testWidgets('english lowercases the trend card label', (t) async { + t.view.physicalSize = const Size(800 * 3, 4000 * 3); + t.view.devicePixelRatio = 3; + addTearDown(t.view.reset); + await _pump(t, const Locale('en')); + await openTrends(t); + expect(find.text('No resting heart rate trend yet'), findsOneWidget); + expect(find.text('No Resting heart rate trend yet'), findsNothing); + // An acronym keeps its case. + expect(find.text('No HRV trend yet'), findsOneWidget); + }); + + testWidgets('german keeps the noun case in the trend card', (t) async { + t.view.physicalSize = const Size(800 * 3, 4000 * 3); + t.view.devicePixelRatio = 3; + addTearDown(t.view.reset); + await _pump(t, const Locale('de')); + await openTrends(t); + expect(find.text('Noch kein Trend für Ruhepuls'), findsOneWidget); + }); }