locale_sweep 0.1.4 copy "locale_sweep: ^0.1.4" to clipboard
locale_sweep: ^0.1.4 copied to clipboard

A CI quality gate for Flutter localization. Runs app flows across locales, device sizes, text scales, and RTL modes, then reports broken screens with screenshot proof.

LocaleSweep #

Localization QA for Flutter — automated.

One function call. Every locale, viewport, and text scale. Screenshots + failure reports.

pub.dev pub points License: MIT Flutter 3.32+

English German Config 2x Scale

Why LocaleSweep? #

Your app looks perfect in English. Then a German user opens Settings and "Benachrichtigungseinstellungen" overflows the row. An Arabic user sees left-aligned text. A Japanese user hits an untranslated screen. You find out from a 1-star review.

LocaleSweep catches these before your users do. It multiplies a single test across every combination of locale, text scale, and viewport — then captures a golden screenshot of each variant and fails only the ones that break.

sweepTest(
  'settings',                    // flow name — used in test labels & screenshot filenames
  builder: () => const MyApp(),  // the widget under test
  arbDir: 'lib/l10n',            // optional — path to your .arb files to detect missing translation keys
  locales: ['en', 'de', 'ar', 'ja'],
  textScales: [1.0, 2.0],
  viewports: [ViewportPreset.phone, ViewportPreset.tablet],
);
// 4 locales × 2 scales × 2 viewports = 16 test cases from one call

What it catches #

# Category How
1 Text overflow Intercepts RenderFlex overflow errors — German compound words, Arabic text expansion, CJK line wrapping
2 Missing ARB keys Finds keys present in app_en.arb but absent in target locale ARB files
3 Placeholder mismatches Verifies {count}, {name}, etc. from base locale appear in every translation
4 Golden regression Pixel-level comparison against committed baselines — catches unintended visual changes
5 Accessibility scaling Renders at 2x text scale to catch layouts that break for large-text users
6 RTL layout Auto-detects Arabic, Hebrew, and Farsi — renders right-to-left

Getting started #

1. Install #

dev_dependencies:
  locale_sweep: ^0.1.4

2. Write a sweep test #

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:locale_sweep/locale_sweep.dart';

void main() {
  sweepTest(
    'onboarding',
    builder: () => const MyApp(),
    arbDir: 'lib/l10n',
    body: (tester) async {
      // optional — interact with the widget before screenshot
      await tester.tap(find.byType(ElevatedButton));
      await tester.pumpAndSettle();
    },
  );
}

3. Run #

# Generate golden baselines (run locally first)
dart run locale_sweep update

# Compare against baselines (run in CI)
dart run locale_sweep run

run never regenerates goldens. update does. This prevents CI from silently accepting broken layouts as the new baseline.


Every variant gets its own golden PNG — locale, text scale, and viewport encoded in the filename.

Onboarding flow

English German 2x Scale Tablet

Configuration flow

English German 2x Scale Tablet

Real golden files from headless Flutter widget tests — not mockups.


CI integration #

GitHub Actions #

- name: LocaleSweep
  run: dart run locale_sweep run --github-pr
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Posts a PR comment with a failure table, locale summary, and inline screenshot links. Updates the same comment on re-runs.

CLI reference #

dart run locale_sweep run                         # Compare goldens, fail broken variants
dart run locale_sweep run --flows onboarding      # Run specific flows only
dart run locale_sweep run --github-pr             # Post results as a PR comment
dart run locale_sweep update                      # Regenerate golden baselines
dart run locale_sweep update --flows settings     # Update specific flows only

Configuration #

Create locale_sweep.yaml in your project root for shared defaults:

locales: [en, de, ar, ja]
text_scales: [1.0, 2.0]

viewports:
  - { name: "375x667", width: 375, height: 667 }
  - { name: "768x1024", width: 768, height: 1024 }

arb_dir: lib/l10n
screenshot_dir: .locale_sweep/screenshots
report_dir: .locale_sweep/reports

Or override per-flow directly in sweepTest():

sweepTest(
  'checkout',
  builder: () => const CheckoutPage(),
  locales: ['en', 'de'],
  textScales: [1.0, 1.5, 2.0],
  viewports: [ViewportPreset.phoneSmall],
  arbDir: 'lib/l10n',
);

API reference #

sweepTest() parameters #

Parameter Type Default Description
flowName String required Identifies this flow in test labels and screenshot filenames
builder Widget Function() required The widget to render
body Future<void> Function(WidgetTester)? null Optional interactions to run after the widget is pumped
locales List<String>? from config BCP-47 locale codes (e.g. 'en', 'de', 'ar')
textScales List<double>? from config Text scale factors to test
viewports List<ViewportPreset>? from config Screen dimensions to test
arbDir String? from config Path to .arb translation files for missing-key analysis
captureScreenshots bool true Whether to save golden screenshots

ViewportPreset built-in presets #

Preset Dimensions Use case
ViewportPreset.phoneSmall 375 x 667 iPhone SE, compact phones
ViewportPreset.phone 393 x 852 Standard modern phones
ViewportPreset.phoneWide 412 x 915 Tall/wide phones
ViewportPreset.tablet 768 x 1024 iPad, Android tablets

Custom: ViewportPreset(name: '1280x800', width: 1280, height: 800)


Output #

.locale_sweep/
  report.md               # Markdown failure table + locale summary
  report.json             # Machine-readable results for CI
  screenshots/
    onboarding_en_393x852.png
    onboarding_de_2.0x_393x852.png
    onboarding_ar_768x1024.png
    settings_en_375x667.png
    ...

How it works #

Step What happens
1 sweepTest() expands into locales x scales x viewports individual testWidgets calls
2 Each test configures the Flutter test view with the target viewport, locale, and text scale
3 Your widget is pumped inside Directionality + MediaQuery wrappers
4 OverflowDetector hooks into FlutterError.onError to capture RenderFlex overflow
5 matchesGoldenFile saves or compares the screenshot
6 ArbAnalyzer runs static analysis on ARB JSON files — no rendering needed
7 Results are recorded before assertions — screenshots always exist, even for broken variants

Real-world validation #

Tested against two popular open-source Flutter apps:

App Stars Result
Spotube 48k+ 0 issues across de/ar/ja/fr/es/ko/zh — zero false positives
wger 960+ 165 missing Arabic keys, 1 placeholder mismatch, 301 missing Hebrew keys — all real bugs

Zero noise on complete translations. Real findings on incomplete ones.


Test suite #

86 tests across 6 test files. Run with flutter test.

Test file Coverage
arb_analyzer_test.dart Missing keys, placeholder mismatches, metadata filtering
overflow_detector_test.dart RenderFlex capture, pixel extraction, handler restore
sweep_variant_test.dart RTL detection, label formatting, screenshot paths
report_test.dart JSON structure, markdown tables, locale summary
sweep_integration_test.dart End-to-end: clean pass, broken ARB, matrix variant count
real_world_arb_test.dart Spotube + wger ARB analysis — zero false positives on real apps

MIT License

8
likes
0
points
656
downloads

Publisher

unverified uploader

Weekly Downloads

A CI quality gate for Flutter localization. Runs app flows across locales, device sizes, text scales, and RTL modes, then reports broken screens with screenshot proof.

Repository (GitHub)
View/report issues

Topics

#flutter #localization #testing #rtl

License

unknown (license)

Dependencies

args, flutter, flutter_test, http, path, yaml

More

Packages that depend on locale_sweep