After localization, the most painful part is fonts. I want one shared font for the entire UI—no manual switches and no “tofu” squares. Below is my working, fast, and legally clean pipeline.
1) The base: Google Fonts
I pick typefaces on [fonts.google.com]—licenses are transparent, language coverage is visible, and style consistency is easy to judge. I localize into:
en, ru, uk, ar, zh, cs, da, nl, fi, fr, de, he, hi, hu, id, it, ja, ko, nb, pl, pt, ro, sk, es, sv, th, tr, vi
Note: you almost always need separate families for th, vi, hi (Devanagari), zh, ja, ko.
2) Building a “combine harvester” from multiple TTF/OTF files
The goal is one file with all glyphs. From a dozen files (by script), I assemble a single TTF like this:
-
Open all families in tabs in FontCreator.
-
In each tab, select all glyphs → Edit → Paste Special… into the “main” font.
-
Enable Glyphs with the same name and Add non-matching — missing symbols get added and existing ones aren’t overwritten.
- If I need to replace certain glyphs (usually digits), I do a regular paste into the target glyph I want to override.
Result: one TTF “for everything.” It works in Defold, but it tends to be heavy. Next step—optimization.
3) A whitelist of characters from the localizations
I want exactly the set of characters that actually appears in the game’s text. I gather it with a Google Apps Script (in the localization spreadsheet):
/**
* Prints a single line: PREFIX + all unique characters from all sheets (no separators).
*/
function printCharsInlineAllSheets() {
const PREFIX = "-=.1234567890"; // your “must-have” symbols: HUD, counters, etc.
const INCLUDE_HIDDEN_SHEETS = false; // true — include hidden sheets
const IGNORED_CHARS = new Set(['\n', '\r', '\t']);
const ss = SpreadsheetApp.getActive();
const sheets = ss.getSheets().filter(sh => INCLUDE_HIDDEN_SHEETS || !sh.isSheetHidden());
const seen = new Set(PREFIX.split(''));
const extra = [];
sheets.forEach(sh => {
const values = sh.getDataRange().getDisplayValues();
values.forEach(row => row.forEach(cell => {
for (const ch of cell) { // iterate over Unicode characters
if (IGNORED_CHARS.has(ch)) continue;
if (!seen.has(ch)) { seen.add(ch); extra.push(ch); }
}
}));
});
// stable locale-aware sort — convenient for reproducibility
const collator = new Intl.Collator(undefined, { usage: 'sort', sensitivity: 'variant' });
extra.sort(collator.compare);
const line = PREFIX + extra.join('');
Logger.log(line); // open Logs and copy the line
}
I run it, copy the line from the logs—that’s my whitelist.
4) Hooking it up in Defold
In the .font resource I point to the combined TTF/OTF, set the size, and paste the string from the script into Characters. I get a neat atlas with only the glyphs I need—no extra weight.


