Before each battle, the characters talks in short dialogue scenes. As these scenes will represent a significant part of the game, I wanted to include support for multiple languages in the game system. I expected encoding issues (and wasn't disappointed!), so I wanted to incorporate this early in the development.
In English, no surprises, everything displays correctly:

However, I started encountering some display issues with accents in French. I noticed that the len
argument of the method playdate->graphics->drawText(const void* text, size_t len, PDStringEncoding encoding, int x, int y)
is in fact not the number of bytes to display but the number of characters. So when I passed strlen(line)
(i.e. the buffer length), it displayed a few extra characters.
Fortunately, I have some knowledge of UTF-8, and was able to quickly write a method to count the number of characters in a sentence:
uint32_t MELUTF8StringCodePointCount(const MELChar * _Nullable source, int length) {
if (source == NULL) {
return 0;
}
uint32_t count = 0;
for (uint32_t index = 0; index < length && source[index] != '\0'; index++) {
const uint32_t entry = source[index] & 0xFF;
if (entry <= 127) {
// ASCII
count++;
}
// isUTF8Wagon is ((source[index] & 0xFF) >> 6) == 2
else if (entry >> 5 == 6 && isUTF8Wagon(source, index + 1)) {
// 2 bytes
count++;
index++;
} else if (entry >> 4 == 14 && isTrailedByCountUTF8Wagon(source, index, 2)) {
// 3 bytes
count++;
index += 2;
} else if (entry >> 3 == 30 && isTrailedByCountUTF8Wagon(source, index, 3)) {
// 4 bytes
count++;
index += 3;
} else {
// Encoding error
playdate->system->logToConsole("Unable to count UTF-8 characters: encoding error, invalid UTF-8 value.");
}
}
return count;
}

Supporting Japanese wasn't as straightforward as I imagined.
First problem: the font. Fortunately, others have been there before me. A big thanks to @matt and @hunty for the Japanese fonts.
Second problem: Japanese doesn't have spaces!
In English and French, the text reflow was handled by looking for the next space or the end of the sentence. If the displayed text size + the next word exceeded the width of the screen, the word would move to the next line. But in Japanese, it displayed nothing because the sentence didn't fit in width. So now, I display letter by letter when the language is Japanese.
The len
argument also took its revenge! Naturally, the one in playdate->graphics->getTextWidth(LCDFont* font, const void* text, size_t len, PDStringEncoding encoding, int tracking)
is also in the number of characters and not in the number of bytes, but I had forgotten to modify the code here as well >_<
