When you fix a string, do you open the translated locales too?
I fixed the default language and remembered it as done. So four locales' checkout screens were selling a feature this app does not have.
What I was doing
I ported several apps to another platform. The originals carry platform-specific feature names and billing copy as strings, and porting means replacing those with the new platform's equivalents.
I believed I had. As a check, I swept the string resources across every app.
127 leftovers — and that number is the trap
A regex sweep finds 127. Of those, 46 are actually referenced from code.
The other 81 are dead strings left behind by the port and never render. Deleting them means a bulk edit across 17 locales — risk with no upside — so I left them.
That's the central judgment of this audit. A raw count doesn't tell you what to do. Only after filtering by "does it render?" does the list drop to 46, and only then does an ordering appear.
The 46 that were live
The worst was one app's checkout screen.
The default locale and Korean had been updated to the new platform's name. Four locales — French, Japanese, Simplified and Traditional Chinese — still carried the original platform's health service name.
That string renders on the screen immediately before payment. Users in those languages were making a purchase decision based on a feature the app does not have.
Another app had the original platform's cloud service name sitting where "cloud sync" belongs on its paywall — and five locales were using that English string with no translation at all.
And two apps' billing notices said "you will be charged through your other-platform account." That is not only untrue, it's plausibly a store policy violation — the same family as the reason those apps were rejected.
The test is two stages
# Stage 1 — candidates (catch the no-space forms too)
pattern = r"Brand\s*[^\s,.。、·]{0,12}|DeviceName|CloudName"
# Stage 2 — is it actually used?
if re.search(rf"R\.string\.{key}\b", all_code):
real_defects.append(key)Without stage 2 you're sitting with a list of 127. That list produces no priority.
You also need an exclusion list: the developer's email domain, legal document URLs, and ordinary nouns used as examples in a word game.
Would you delete the 81 dead strings?
I didn't.
- They never render.
- Removing them is a bulk edit across 17 locales.
- It adds regression risk and returns nothing.
Same lesson as the deploy script that nearly undid a day's work. A bulk edit is itself a risk asset. If there's no value, don't make one.
Three-line self-check
- When you change one string, do you grep all of
values-*? Fixing the default language leaves you with the memory of a fix. - Does your scan report "how many" or "how many render"? Without a reference filter, a list never becomes an action.
- When did you last read your paywall copy locale by locale? That's the screen where an error costs the most.
The honest part
My first regex missed the no-space forms. In Chinese strings the brand name runs straight into the next character, and a pattern assuming a space never matches. It only surfaced on the second pass — meaning my first "full sweep" was not full.
There were false positives too. One app's strings mention the original platform's device name, and that was correct usage — an app for that device genuinely exists and can exchange files. A brand name appearing is not automatically a defect. The developer's email address contains a cloud service domain, which also tripped the pattern.
And this audit only looked at strings. Whether icons, screenshots or store listings still carry traces of the original platform, I did not check this time. The same week I found store descriptions claiming a feature the app doesn't have, so that direction is still open.
If you've ported an app, run grep -r "old-platform-name" res/ and then count how many of those are referenced in code. The gap between the two numbers is your priority list.