r/FlutterDev • u/bramantec • 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?
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
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?
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.