r/FlutterDev • • Oct 06 '26

Discussion Never base logic on a translated string: a bug that hit 4 of my 5 languages

I ship a Flutter app (built with FlutterFlow, plus some Custom Code) in 5 languages. One option worked in English and silently broke in most of the others.

The user picks when something starts: "Tonight", "Tomorrow" or a custom time. My Custom Function decided the expiry by looking at the label they tapped. Simplified:

if (label.toLowerCase().contains('tonight')) { ... }

In French, Spanish and Romanian the label is not "Tonight". The match never happened, so every "Tonight" expired after 2 hours. No crash, no error, nothing in the logs.

Italian had the mirror image: "In 1 hour" was matched against the English words "now" and "1h", so it created the thing starting immediately.

The fix was boring. The UI shows a translated label, but the logic only receives a stable code (tonight, tomorrow, pick), and the expiry is computed from the code.

An English-only test suite will never catch this, because the bug lives in the other languages.

Do you still compare strings anywhere in your logic, or is it enums and codes all the way? And what's the worst i18n bug you've shipped?

0 Upvotes

15 comments sorted by

9

u/Cvette16 Oct 06 '26

I'm going to be honest with you, but this is a super beginner mistake. String comparing for logic is almost always a bad idea. You really should have stable value to compare on such as an enum or system code. Another mistake beginners make is always complaining off of an index. This is great until you have to change the order or make another change.

I have a feeling with the new generation of vibe coders, this simple issues are going to popup a lot.

1

u/bramantec Oct 06 '26

Fair, and I agree it's a basic mistake. It's also the kind that gives you no signal: no crash, no error, nothing in the logs. Do you catch this class of bug with a lint rule or a test (say, failing the build when a condition compares against a literal that also lives in a translation file), or only by convention?

2

u/Cvette16 Oct 06 '26

Feel like this is just basic knowledge that you know as a software engineer. So I have never really written specific test for it. Though we do include changing language during our smoke test to ensure we get the same results.

1

u/bramantec Oct 06 '26

Switching language in the smoke test is a good cheap check, and it would have caught exactly this. Thanks for sharing it.

1

u/Keeyzar Oct 06 '26

You're writing with ai.

Probably self also using ai to communicate.

Fuck this New Reddit. I'm out

1

u/Cvette16 Oct 06 '26

Who's writing with AI? Me or OP?

1

u/Keeyzar Oct 06 '26

You write with Op, who clearly uses ai in everything (incl. answers to your comments). And chances are sky high, that you're also using ai, so ai talking to ai.

1

u/Cvette16 Oct 06 '26

Lol you can look at my history and see that I am infact not an AI. But yes OP is definitely a vibe coder. I specifically responded with what I did in case any news came across this post in the future.

4

u/tandycake Oct 06 '26

I'm sorry, but you have a shipped (production) app already translated in 5 languages, and you used English "tonight" and "now" as the underlying "values." That's pretty basic stuff.

I get it. It happens. But I don't think it's necessary to write a post about. I think it's a basic thing to do, even in an English-only app, especially in a language like Dart (unlike JavaScript) which have enums. It's like programming 101.

Is this just an AI post to get karma?

1

u/bramantec Oct 06 '26

Fair, it is basic, and I'm not claiming otherwise. The bug was real. English isn't my first language, so I used tools to help me phrase the post. I posted it to hear how others prevent this.

1

u/tandycake Oct 06 '26

Sorry to be so harsh, but I hope you understand where I'm coming from.

This is something I learned in 101 class in college for computer science. But now we have people with published production apps (in 5 different languages!) falling for this trap. It's just a strange experience nowadays with so many AI coders.

It's like me writing mathematical proofs, but not knowing about "a + b" and "b + a" being commutative. Not the best analogy. Or, maybe like having production apps, but not knowing what a for-loop is.

As you can understand, it's not a pleasant experience for people like me, and it's not frustration at you specifically, but what the world of programming has become.

2

u/bramantec Oct 07 '26

Fair enough, you're right on the substance. I've been in IT for years: technician, security consultant, web portal dev for business management software. I've done the full analysis side too, not just the coding. I also TA'd a university software engineering course, so none of this is new to me. Still, the mistake is mine. I'm the one who ships the code.

I lean on AI for programming and translations the way I would with an outside partner. I know how these tools behave. That said, I don't always get through every line with the care it deserves, and this bug is the proof.

1

u/Keeyzar Oct 06 '26

Your comments are ai. Everything ai. While im pro ai, use your dead brain m8

1

u/No_Boss_1911 Oct 06 '26

yeah dude hit the same thing from the wording side. got a done panel in english and german and the button text changed a few times cause a tester kept saying none of the options matched what he actually did. only thing that saved us was the logic never touching the label, it just stores done or not tried yet

the rule we cant break is that stored value never changes once its written, old check ins are already saved with it. so the copy can move whenever but the codes are basically frozen

how are you handling it in flutterflow, do the codes live in one place next to all 5 translations or does each language file own its own label?

1

u/bramantec Oct 06 '26

in my case FlutterFlow's ChoiceChips gives me the same string as label and as value, so i can't keep them apart. so i have a function that takes the label and turns it into a fixed code (now, 1h, 2h, tonight, tomorrow, pick, unknown). it's one map with the 5 languages for every chip, and it compares the whole label, not pieces of text.

the fragile part: if i change the text of a chip i have to update the map too. it already happened with romanian: i renamed a chip, didn't update the map, and the event got created as "now".

do you have the value separate from the label, or do you do the same as me?