Type-to-answer in six languages
Globdrop can be played in Catalan, Spanish, English, Chinese, Arabic and Japanese. In most of the game, language is only a matter of display. In the flag scenarios’ Write mode you type the answer, and then language becomes a matching problem. How Write mode works explains the round from the player’s side. This post is about the engineering underneath: which text exists in which language, what the normaliser does to each script, and where it fails.
Three layers, three levels of coverage
“Six languages” means different things in different parts of the code:
| Layer | Where it lives | Languages |
|---|---|---|
| Interface text (buttons, prompts, feedback) | src/engine/i18n.ts | All six |
| Share messages | src/engine/share.ts | All six |
| Place names in the map scenarios | public/data/scenarios/*.json | All six keys on all 1,380 places. For the 434 places outside Catalonia they’re real translations. For the 946 Catalan municipalities, the Chinese, Arabic and Japanese keys repeat the Catalan name |
| Flag names | public/data/flags/*.json | Catalan, Spanish and English only, for all 182 flags |
For the map scenarios that’s enough, because you never type a place name there: you read it and click. The flag scenarios are where the gap shows.
Fallback to English
Every name goes through one function:
export function getLocalized(obj: LText | undefined | null): string {
if (obj == null) return '';
if (typeof obj === 'string') return obj;
if (obj[currentLang]) return obj[currentLang]!;
if (obj['en']) return obj['en']!;
// Fallback to first available translation
const keys = Object.keys(obj);
return keys.length > 0 ? obj[keys[0]!]! : '';
}
With the interface in Japanese, a flag name has no ja key, so you get the English one. That’s what the suggestion list shows, and what the feedback says after a wrong answer. The placeholder above the box, meanwhile, comes from i18n.ts and is in Japanese. It asks you to type the name of the country or region.
The check itself doesn’t depend on the interface language at all. handleTextAnswer in src/engine/flag-engine.ts compares your answer with every name the flag has. In practice, then, Write mode accepts Catalan, Spanish and English in every interface language, and nothing else.
The normaliser, script by script
Until late September 2026, both the check and the suggestion list used the same four steps. The code has changed since; the last section says how.
private normalizeText(text: string): string {
return text
.toLowerCase()
.trim()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, ''); // Remove accents/diacritics
}
NFD splits a precomposed character into its base and its combining marks. The last line then deletes the marks in the Unicode block Combining Diacritical Marks, U+0300 to U+036F. We ran the function on each script to see what it actually did.
Latin: worked for accents, not for punctuation
For the three languages the flags have, this covers most accents: é, à, ñ, ç and ü all lose their marks. Turkish İ works too: lower-casing it gives i plus a combining dot above (U+0307), which the same line removes.
What it left alone was punctuation, and Catalan has some of its own. Gal·les kept its middle dot, Sud-àfrica its hyphen, and l'Orgull its apostrophe, so each had to be typed exactly. A curly apostrophe (’) didn’t match the straight one in the data. Neither did repeated spaces inside the text, since only the ends were trimmed. All of these but the middle dot are handled now.
Chinese: nothing to compare with
Chinese has no letter case and no combining marks in that range, so normalisation changes nothing. The problem is earlier: there are no Chinese flag names to compare with. 中国 for China is always wrong.
Two more cases would matter as soon as names were added. Normalisation doesn’t map traditional characters to simplified ones: 中國 and 中国 stay different strings. And many Chinese and Japanese input methods can produce full-width Latin letters. We checked that USA survived the old function as full-width usa, because NFD doesn’t fold character width, so a player typing the English name in full-width mode got it wrong. With NFKC first, it now comes out as usa.
Japanese: kana survived, widths didn’t
NFD also splits Japanese voiced kana. が becomes か plus U+3099, the combining voiced sound mark. That mark is outside the range the function deleted, so it stayed, and が was still が. That’s the right result: the mark changes the sound, so it isn’t an accent to throw away. Half-width katakana were not folded either: テスト stayed half-width, while the Unicode compatibility form NFKC turns it into テスト. Folding width is the job of NFKC, and the code used only NFD.
Arabic: vowel marks and direction
Arabic short-vowel marks (harakat) are combining characters too, but they live in another block, around U+064B to U+065F. The function didn’t touch them, so a name stored with vowel marks wouldn’t have matched the same name typed without them.
Direction was the bigger gap. Nothing in the code set dir="rtl". The <html> element was fixed at lang="ca", whatever language you picked, and no CSS depended on direction. Arabic text displays correctly within each line, because the browser’s bidirectional algorithm orders the characters, but the layout stayed left to right: the text box, the Submit button and the suggestion list all kept their left-to-right positions. The text box had no dir either, so an Arabic answer, if one could be right, would be typed into a left-to-right field.
Autocomplete and input methods
The suggestion list is rebuilt on every input event: it normalises what’s in the box and keeps up to five names that contain it. For Latin text that works as you’d expect.
Chinese and Japanese are usually typed through an input method, where you compose a word and then confirm it. The code doesn’t listen for compositionstart or compositionend, and until late September the Enter handler didn’t check event.isComposing either; now it does. We haven’t tested what that does on each browser. The likely effects are suggestions that update on half-composed text, and an Enter meant to confirm a word being taken as a submit. Since answers in those scripts can’t be right today, this matters less now than it will once names are added.
Numbers, fonts and reloads
A few smaller details follow the same pattern:
- Numbers.
formatPointsandformatDistanceinsrc/engine/scoring.tspicked a locale fortoLocaleString:en-USfor English,es-ESfor Spanish, andca-ESfor everything else. Chinese, Arabic and Japanese players saw scores in Catalan format, with a dot for thousands (3.250), where a Chinese locale would write3,250. - Fonts. The page loads Inter and Outfit from Google Fonts. The Chinese, Japanese and Arabic text falls back to whatever system font the device has.
- Switching. Changing language stores the choice in
localStorageand reloads the page, so every screen is redrawn in the new language, with nothing to update in place.
What fails, in one list
With the code as it was on 24 September 2026, before the changes below:
- Answers in Chinese, Arabic or Japanese script are always wrong, because flag names only exist in Catalan, Spanish and English.
- In those three interfaces, suggestions and corrections appear in English.
- Hyphens, the Catalan middle dot, apostrophes (straight or curly), brackets and inner spaces must match exactly.
- Full-width Latin letters, half-width katakana and Arabic vowel marks aren’t normalised.
- There’s no right-to-left layout and no
langattribute beyond Catalan. - Input-method composition isn’t handled, and hasn’t been tested.
The tests don’t cover any of this. The Write mode test in tests/unit/flag-engine.test.ts uses Catalan and Spanish names only.
What we changed
After writing the list above we worked through most of it in the game’s repo:
- One normaliser.
normalizeTextnow lives once, insrc/engine/text.ts, and both the check and the suggestions use it. It applies NFKC first, soUSAbecomesusaandテストbecomesテスト. Then it lower-cases, removes Latin combining marks and Arabic short-vowel marks, turns hyphens into spaces, drops dots and apostrophes (straight or curly) and collapses spaces. The Japanese voiced marks are still kept. - Extra spellings. A flag can list aliases, accepted in any interface language:
USAandUK,Euskadi,País Valencià,Hollandand a few more. A name with a bracket accepts each side, so “United Nations (UN)” takes “United Nations” or “UN”. - Direction.
<html>getslanganddirfrom the chosen language, so the Arabic interface is laid out right to left. The map itself isn’t mirrored. - Input methods. Enter no longer submits while an input method is composing a word.
- Numbers. Scores and distances use the chosen language’s locale.
- Tests.
tests/unit/text.test.tsruns the normaliser on each case above, and the Write mode test now covers an alias and an empty answer.
What’s still missing is the biggest item: flag names in Chinese, Arabic and Japanese. Until they exist, an answer typed in those scripts is still wrong, and the suggestions still appear in English. The Catalan middle dot and inner brackets must still match exactly, and we still haven’t tried composition on real input methods on each browser.
How the rest of the game is put together is in how Globdrop is built. The flag sets themselves are described in curating Globdrop’s flag scenarios.