Some testing work starts with a bug.
This one started with a client requirement.
They already had an internal translation process, and part of that process on the web side was being able to see not just the rendered text, but the key behind it. That way, when a translator changes one string, they have a much clearer idea of where it appears and whether that same key is reused somewhere else.
They wanted the same kind of visibility on Android.
At first glance, that sounds like a narrow requirement. In practice, it turned into one of the more useful bits of UI infrastructure I have added in a while, because it stopped being just about translation and quickly became useful for designers too.
Once you can snapshot the same screen across multiple locales, multiple font scales, and a “show me the key instead of the final text” mode, you stop treating localisation and accessibility as abstract concerns. You can just look at the output and see what survives contact with reality.
That was the real value.
Not a dramatic testing story. Not a heroic “we found dozens of catastrophic bugs” story. Honestly, the surprising part is that it all went pretty smoothly, which is rare enough that it is worth writing down.
I almost distrusted it at first for exactly that reason.
Most testing infrastructure work has at least one stretch where you start wondering whether the whole thing is about to turn into an awkward maintenance burden. This did have a couple of rough edges, but it never really tipped over into that territory. It mostly just kept proving useful.
The Requirement Was Not Really “Take Screenshots”
The request was straightforward on paper:
- show translators where strings appear
- show which key is being used
- make it easier to review changes across languages
- help catch problems with font scaling before they turn into last-minute cleanup
If you reduce that to “screenshot testing,” it sounds more ordinary than it really was.
The more interesting requirement was this:
how do you make translation review less dependent on imagination?
A spreadsheet of keys is useful up to a point. A preview in isolation is useful up to a point. But once translators and designers can look at a real screen variant and see both the layout and the string identity, the conversation gets much better.
Instead of asking:
“what does this key map to again?”
you can ask:
“if we change this key here, what else are we implicitly changing?”
That is a much better kind of review.
I Used Compose Previews As The Source Of Truth
The approach I liked most was using Compose previews as the source of truth.
That meant I did not need to hand-maintain a giant screenshot test list somewhere else. If a screen already had a useful preview, I could snapshot it. If it needed better coverage, the right fix was usually to improve the preview rather than bolt more configuration onto the test harness.
I liked that constraint because it kept the work honest. If a preview was too weak to be useful for screenshots, it was probably too weak to be that useful in the IDE either.
The matrix itself was simple:
- English
- Arabic
- multiple font scales
- a key-display mode for translation review
The preview annotation looked roughly like this:
There was one small trick in there.
The “Keys” preview used an unsupported locale on purpose so the UI would fall back to showing key values instead of translated text. That gave translators a screen-level view of which string was being used where, without needing a separate mode built purely for manual inspection.
That part ended up being especially useful because it mirrors how they already work on the web side. It did not force them into an Android-specific review model.
That mattered more than I expected. A lot of internal tooling friction comes from making one team adapt to another team’s conventions. This felt better because it met an existing review habit halfway instead of asking translators to learn a new mental model just because the platform had changed.
The Key View Was More Useful Than It Sounds
Showing raw keys in a UI sounds a bit odd until you have to maintain a translation set at scale.
If two places in the app happen to use the same key, that can be perfectly reasonable. It can also be a quiet source of accidental coupling. A translator updates one value, assuming it only affects one screen, and later discovers it also changed a dialog somewhere else that happened to share the same string resource.
The key preview does not solve that by itself, but it makes the coupling visible.
That is the part I liked.
It did not just answer “is this translated?” It helped answer:
- which exact string is this?
- where is it being used?
- is this usage more shared than we thought?
- if we change it, who else do we need to warn?
That turns a screenshot review from passive confirmation into something much closer to real collaboration.
Font Scale Was The Other Dimension That Made This Worth Doing
If you already use `@PreviewFontScale`, this probably sounds obvious.
I still think it is worth stating plainly: font scale coverage buys you more than people expect.
A layout can look completely fine at default scale and become much less convincing once the text grows. That does not always mean it is broken, but it often reveals where the UI was only “working” because the content happened to be compact enough in the happy path.
The useful part here was not accessibility compliance in the abstract. It was being able to show translators and designers the practical consequences of longer content and larger text at the same time.
That is where the screenshots earned their keep.
A short label in English at `1.0x` is not a serious test. A longer phrase in another language at `1.5x` or `2.0x` is much closer to reality.
At the very least, if you are already using `@PreviewFontScale`, I think this is one of the better arguments for taking it seriously.
It also made review conversations much less theoretical. Nobody had to imagine what “this might get tight at larger sizes” meant. They could just look at the screen and decide whether it still held up.
The Output Needed To Be Useful To People, Not Just Diffs
One thing I wanted to avoid was generating a pile of screenshots that only engineers could tolerate.
The raw snapshot output is fine for verification, but it is not always the nicest thing to hand to non-engineers. So alongside the snapshot report, I generated a small HTML gallery that grouped previews in a way translators and designers could review comfortably.
The workflow was roughly: record the Paparazzi previews, then generate the review gallery from the report output.
The gallery generation script is here:
And the test side looked like this:
That is not an especially glamorous setup, but it did the job.
The engineering side kept proper snapshot coverage.
The human-review side got a gallery that was much easier to skim.
That split mattered because the main audience here was translators and designers, not just me.
That is an easy thing to say and a harder thing to design for. Engineering output often becomes “technically available” to other people without actually being pleasant for them to use. I wanted to avoid that trap.
The Story Was Happily Short On Drama
I almost feel guilty saying that, because technical write-ups often read better when something catches fire.
But the honest version is that this rollout was smoother than most test infrastructure work I have done.
There were two practical wrinkles.
The first was around the preview tooling itself. Early on, there was some confusion around preview configuration, and the fix we ended up contributing was not some deep runtime change. It was much simpler: the documentation needed to be clearer, especially around including `fontScale` in the `deviceConfig` example and showing a sensible file naming convention example in the README.
That may sound minor. It was still useful.
A lot of tooling friction is not “the library is broken.” Sometimes it is just that the path to using it correctly is less obvious than it should be. In this case, cleaning that up made the setup much more legible, especially if you are using `@PreviewFontScale` and expect the output naming and device configuration to reflect that properly.
The second wrinkle was scrollable content.
That is one of those things you notice quickly once you start relying on previews for more than just compact components. Some screens do not fit neatly into a single static capture, and previews do not magically solve that for you.
So I needed a small workaround there.
Nothing especially exotic, just enough structure so scrollable content could be captured in a way that was still useful for review. Once that was in place, it worked fine. But it was a good reminder that preview-driven testing is only as honest as the preview itself. If the preview hides the part of the screen people actually need to inspect, your coverage is technically present and practically weak.
The small workaround for scrollable preview content is here:
That is a pattern I would watch carefully on any future project.
The Real Maintenance Cost Is Preview Quality
This is the part I would emphasise most if you want to do something similar.
The real maintenance burden is not the screenshot harness.
It is the quality of the previews.
If previews are treated as decorative IDE extras, a setup like this becomes fragile very quickly. If previews are treated as first-class review surfaces, the whole thing becomes much more valuable.
That was probably the biggest mindset shift in the whole thing. Once I stopped thinking of previews as just an IDE convenience and started treating them as review assets, a lot of the surrounding decisions got easier.
That means the preview has to be intentional:
- realistic content
- sensible state coverage
- representative long text
- meaningful locale choices
- a clear reason to exist
That last part matters.
A weak preview creates weak screenshots. A strong preview becomes reusable documentation, reusable test coverage, and reusable review material all at once.
That is what made this approach feel worth keeping.
Why I Would Do It Again
I would absolutely do this again, mainly because it made a specific client requirement turn into something broader and more durable.
Yes, it helped translation review.
But it also improved design review, made font-scale issues easier to surface early, and created a better shared artifact than a list of string resources ever could.
The key-mode preview was a particularly good fit because it respected an existing workflow instead of inventing a new one just because this was Android.
And the whole thing stayed grounded. It did not need a dramatic failure story to justify itself. It just needed to make a recurring piece of review work easier and more reliable.
That is enough.
What I Would Recommend If You Try This Yourself
If you want to build a similar setup, the sequence I would trust is:
- Start by deciding who the screenshots are actually for.
- Use previews as the source of truth instead of creating a separate manual matrix.
- Include font-scale coverage early, not as an afterthought.
- Add a key-display mode if translation review needs string identity, not just final text.
- Be honest about scrollable screens and make sure the preview still shows what reviewers need to see.
- Treat documentation as part of the tooling, especially if preview configuration is easy to misread.
That is probably the most boring-sounding lesson here, but also one of the most useful.
A lot of testing infrastructure succeeds or fails on whether the surrounding workflow is clear enough for people to trust it.
This one ended up being trustworthy.
And because of that, it ended up being more useful than the original requirement even asked for.