mkxp-sunshine/src/i18n.cpp
2026-07-14 12:43:43 -04:00

261 lines
6.1 KiB
C++

/*
Really hacked-together naiive implementation of gettext
*/
#include "i18n.h"
#include <stdlib.h>
#include <stdio.h>
#include <SDL3/SDL_stdinc.h>
char** strdict = 0;
unsigned int nStr = 0;
const int MAX_LANGUAGES = 20;
const int LANGCODE_SIZE = 16;
const int LANGFONT_SIZE = 128;
char* currentLocale = 0;
struct LanguageFontAndSize {
char* lang_code = 0;
int size = 0;
char* font_name = 0;
};
LanguageFontAndSize** languageMetadata = 0;
const char* findtext(unsigned int msgid, const char* fallback) {
if (msgid >= nStr) {
return fallback;
} else {
return strdict[msgid];
}
}
void unloadLocale() {
for (unsigned int i = 0; i < nStr; i++) {
SDL_free(strdict[i]);
}
SDL_free(strdict);
SDL_free(currentLocale);
strdict = 0;
nStr = 0;
}
void unloadLanguageMetadata() {
if (languageMetadata) {
for (int i = 0; i < MAX_LANGUAGES; ++i) {
LanguageFontAndSize* ldata = languageMetadata[i];
if (ldata) {
if (ldata->font_name) {
SDL_free(ldata->font_name);
}
if (ldata->lang_code) {
SDL_free(ldata->lang_code);
}
SDL_free(ldata);
}
}
}
SDL_free(languageMetadata);
}
void loadLanguageMetadata() {
if (languageMetadata) {
unloadLanguageMetadata();
}
// char line[256];
char line[1024];
languageMetadata = (LanguageFontAndSize**) SDL_calloc(MAX_LANGUAGES, sizeof(LanguageFontAndSize*));
FILE* fontsFile = fopen("Languages/internal/language_fonts.ini", "r");
if (fontsFile) {
int languageMetadataIndex = 0;
while (fgets(line, 1024, fontsFile)) {
char* indexOfEquals = SDL_strchr(line, '=');
if (indexOfEquals) {
// splitting the string in place here
indexOfEquals[0] = 0;
char* indexOfFontName = indexOfEquals + 1;
// remove new line from end of font name
char* indexOfNewLine = SDL_strchr(indexOfFontName, '\n');
if (indexOfNewLine) {
indexOfNewLine[0] = 0;
}
// make new strings for code and font name
char* langCode = (char*)SDL_calloc(LANGCODE_SIZE, sizeof(char));
char* langFont = (char*)SDL_calloc(LANGFONT_SIZE, sizeof(char));
SDL_strlcpy(langCode, line, sizeof(line));
SDL_strlcpy(langFont, indexOfFontName, sizeof(indexOfFontName));
// allocate metadata mem
LanguageFontAndSize* metadata = (LanguageFontAndSize*) SDL_calloc(1, sizeof(LanguageFontAndSize));
metadata->font_name = langFont;
metadata->lang_code = langCode;
// put into metadata array and check if we're going to go over the language limit
languageMetadata[languageMetadataIndex] = metadata;
languageMetadataIndex++;
if (languageMetadataIndex >= MAX_LANGUAGES) {
break;
}
}
}
//fclose(fontsFile);
}
FILE* fontSizesFile = fopen("Languages/internal/language_sizes.ini", "r");
if (fontSizesFile) {
while (fgets(line, 1024, fontSizesFile)) {
int languageMetadataIndex = 0;
char* indexOfEquals = SDL_strchr(line, '=');
if (indexOfEquals) {
// splitting the string in place here
indexOfEquals[0] = 0;
char* indexOfFontSize = indexOfEquals + 1;
// remove new line from end of font size
char* indexOfNewLine = SDL_strchr(indexOfFontSize, '\n');
if (indexOfNewLine) {
indexOfNewLine[0] = 0;
}
int fontSize = SDL_atoi(indexOfFontSize);
for (int i = 0; i < MAX_LANGUAGES; i++) {
// search for corresponding langCode in metadata array to populate font size in the appropriate metadata
LanguageFontAndSize* metadata = languageMetadata[i];
if (metadata && SDL_strcmp(line, metadata->lang_code) == 0) {
metadata->size = fontSize;
break;
}
}
}
}
}
}
int getFontSize() {
for (int i = 0; i < MAX_LANGUAGES; i++) {
LanguageFontAndSize* metadata = languageMetadata[i];
if (metadata && SDL_strcmp(currentLocale, metadata->lang_code) == 0) {
return metadata->size;
}
}
// default to 12?
return 12;
}
char* getFontName() {
for (int i = 0; i < MAX_LANGUAGES; i++) {
LanguageFontAndSize* metadata = languageMetadata[i];
if (metadata && SDL_strcmp(currentLocale, metadata->lang_code) == 0) {
return metadata->font_name;
}
}
// default to terminus?
return "Terminus (TTF)";
}
void loadLocale(const char* locale) {
char line[1024];
char pathbuf[100];
FILE* locfile;
unloadLocale();
currentLocale = (char*) SDL_calloc(128, sizeof(char));
SDL_strlcpy(currentLocale, locale, 128 - 1);
int dictSize = 100;
// currently there are 52, but 100 should be plenty if we ever do add more
strdict = (char**)SDL_malloc(sizeof(char*) * dictSize);
sprintf(pathbuf, "Languages/internal/%s.po", locale);
locfile = fopen(pathbuf, "r");
if (locfile) {
while (fgets(line, 1024, locfile)) {
if (SDL_strncmp("msgstr \"", line, 8) == 0) {
char* lineWithoutMsgid = line + 8;
char* endQuoteAddress = SDL_strrchr(lineWithoutMsgid, '"');
// end string at last quotation mark
if (endQuoteAddress != 0) {
endQuoteAddress[0] = 0;
}
decodeEscapeChars(lineWithoutMsgid);
int lineLen = SDL_strlen(lineWithoutMsgid);
strdict[nStr] = (char*)SDL_malloc(lineLen + 1);
SDL_strlcpy(strdict[nStr], lineWithoutMsgid, sizeof(lineWithoutMsgid));
nStr++;
}
}
fclose(locfile);
}
}
// replaces escape characters with their actual values in-place in a string
// can do it in-place because the replacement values are always smaller than the original value
// it only handles \\ and \", but this can be expanded to include more if needed
void decodeEscapeChars(char* s) {
char* dest = s;
char* src = s;
while (src[0] != 0) {
switch (src[0]) {
// might be an escape character?
case '\\':
switch (src[1]) {
case '\\' :
dest[0] = '\\';
dest++;
src += 2;
break;
case '\"':
dest[0] = '\"';
dest++;
src += 2;
break;
// end of the string?
// so only advance forward 1 character
case 0:
dest[0] = '\\';
dest++;
src++;
break;
// not escape, handle normally
default:
dest[0] = '\\';
dest[1] = src[1];
dest+=2;
src+=2;
break;
}
break;
default:
dest[0] = src[0];
dest++;
src++;
}
}
// cap the end of the string
dest[0] = 0;
}