Localize remaining hardcoded UI strings and persist language preference

Replaces the last hardcoded English strings (initial-scene modals, game
menu save status messages) with locale-loaded text, adds a shared
LOCALE_LIST so the settings dropdown and locale manager stay in sync,
and persists the chosen language into savemeta_t across all platforms.
Also fixes two message buffers that were too small for their longest
translations.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 21:39:24 -05:00
parent 1ddc298a74
commit 7357b4a5df
19 changed files with 417 additions and 50 deletions
+52
View File
@@ -60,6 +60,58 @@ msgstr "Settings"
msgid "ui.game_menu.save"
msgstr "Save"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_success"
msgstr "Game saved."
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_cancelled"
msgstr "Save cancelled."
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_unavailable"
msgstr "Can't save - no save device found."
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_temporary"
msgstr "This session is temporary - no save device was found, so saving is disabled."
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_create_confirm"
msgstr "No save data found. Create a new save?"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_failed_format"
msgstr "Save failed: %s"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_check_failed_format"
msgstr "Can't save: %s"
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.message"
msgstr "No save device found. You can continue, but\nprogress will not be saved."
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.retry"
msgstr "Retry"
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.continue"
msgstr "Continue Anyway"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.message"
msgstr "No save data found. Create a new save?"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.yes"
msgstr "Yes"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.no"
msgstr "No"
msgid "item.potion.name"
msgstr "Potion"
+52
View File
@@ -61,6 +61,58 @@ msgstr "Configuración"
msgid "ui.game_menu.save"
msgstr "Guardar"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_success"
msgstr "Partida guardada."
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_cancelled"
msgstr "Guardado cancelado."
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_unavailable"
msgstr "No se puede guardar: no se encontró ningún dispositivo de guardado."
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_temporary"
msgstr "Esta sesión es temporal - no se encontró ningún dispositivo de guardado, por lo que guardar está deshabilitado."
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_create_confirm"
msgstr "No se encontraron datos guardados. ¿Crear una partida nueva?"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_failed_format"
msgstr "Error al guardar: %s"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_check_failed_format"
msgstr "No se puede guardar: %s"
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.message"
msgstr "No se encontró ningún dispositivo de guardado. Puedes continuar, pero\nel progreso no se guardará."
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.retry"
msgstr "Reintentar"
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.continue"
msgstr "Continuar de todos modos"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.message"
msgstr "No se encontraron datos guardados. ¿Crear una partida nueva?"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.yes"
msgstr "Sí"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.no"
msgstr "No"
#: src/dusk/rpg/item/item.json
msgid "item.potion.name"
msgstr "Poción"
+52
View File
@@ -61,6 +61,58 @@ msgstr "設定"
msgid "ui.game_menu.save"
msgstr "セーブ"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_success"
msgstr "セーブしました。"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_cancelled"
msgstr "セーブをキャンセルしました。"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_unavailable"
msgstr "セーブできません - セーブデバイスが見つかりません。"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_temporary"
msgstr "このセッションは一時的です - セーブデバイスが見つからなかったため、セーブは無効になっています。"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_create_confirm"
msgstr "セーブデータが見つかりません。新しいセーブを作成しますか?"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_failed_format"
msgstr "セーブに失敗しました: %s"
#: src/dusk/ui/frame/game/uigamemenu.c
msgid "ui.game_menu.save_check_failed_format"
msgstr "セーブできません: %s"
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.message"
msgstr "セーブデバイスが見つかりません。続行できますが、\n進行状況は保存されません。"
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.retry"
msgstr "再試行"
#: src/dusk/ui/frame/initial/uiinitialnocard.c
msgid "ui.initial.no_card.continue"
msgstr "続行する"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.message"
msgstr "セーブデータが見つかりません。新しいセーブを作成しますか?"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.yes"
msgstr "はい"
#: src/dusk/ui/frame/initial/uiinitialcreatesave.c
msgid "ui.initial.create_save.no"
msgstr "いいえ"
#: src/dusk/rpg/item/item.json
msgid "item.potion.name"
msgstr "ポーション"
+27 -1
View File
@@ -7,16 +7,42 @@
#include "localemanager.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
#include "save/save.h"
localemanager_t LOCALE;
const localeinfo_t * const LOCALE_LIST[LOCALE_LIST_COUNT] = {
&LOCALE_EN_US,
&LOCALE_JP_JP,
&LOCALE_ES_MX
};
errorret_t localeManagerInit() {
memoryZero(&LOCALE, sizeof(localemanager_t));
errorChain(localeManagerSetLocale(&LOCALE_EN_US));
// saveInit() runs before this (see engine.c) and, on most platforms,
// has already loaded a persisted language choice into savemeta_t by
// this point - see localeManagerGetByIndex().
errorChain(localeManagerSetLocale(
localeManagerGetByIndex(saveGetMeta()->language)
));
errorOk();
}
uint8_t localeManagerGetIndex(const localeinfo_t *locale) {
assertNotNull(locale, "Locale cannot be NULL");
for(uint8_t i = 0; i < LOCALE_LIST_COUNT; i++) {
if(stringCompare(LOCALE_LIST[i]->file, locale->file) == 0) return i;
}
return 0;
}
const localeinfo_t * localeManagerGetByIndex(const uint8_t index) {
if(index >= LOCALE_LIST_COUNT) return LOCALE_LIST[0];
return LOCALE_LIST[index];
}
errorret_t localeManagerSetLocale(const localeinfo_t *locale) {
assertNotNull(locale, "Locale cannot be NULL");
+32 -2
View File
@@ -18,21 +18,51 @@ typedef struct {
extern localemanager_t LOCALE;
/**
* Every locale the game supports, in a fixed, stable order - index into
* this array is what gets persisted as savemeta_t.language (see
* save/savemeta.h), so the order here must never change once shipped
* (only append new locales at the end).
*/
#define LOCALE_LIST_COUNT 3
extern const localeinfo_t * const LOCALE_LIST[LOCALE_LIST_COUNT];
/**
* Initialize the locale system.
*
*
* @return An error code if a failure occurs.
*/
errorret_t localeManagerInit();
/**
* Set the current locale.
*
*
* @param locale The locale to set.
* @return An error code if a failure occurs.
*/
errorret_t localeManagerSetLocale(const localeinfo_t *locale);
/**
* Finds the LOCALE_LIST index matching the given locale's file, by
* content rather than pointer identity (localeinfo_t instances are
* declared `static const` in a header, so the same locale gets a
* distinct pointer in every translation unit that references it).
*
* @param locale The locale to find.
* @return The matching index in LOCALE_LIST, or 0 if not found.
*/
uint8_t localeManagerGetIndex(const localeinfo_t *locale);
/**
* Gets the locale at the given LOCALE_LIST index, clamping to index 0
* (LOCALE_EN_US) if out of range - e.g. a save file's persisted language
* index from a future build with more locales than this one supports.
*
* @param index The index to look up.
* @return The locale at that index, or LOCALE_LIST[0] if out of range.
*/
const localeinfo_t * localeManagerGetByIndex(const uint8_t index);
/**
* Get a localized string for the given message ID.
*
+1
View File
@@ -20,6 +20,7 @@ static void _saveEagerLoadComplete(errorret_t result, void *user) {
errorret_t saveInit(void) {
memoryZero(&SAVE, sizeof(save_t));
SAVE.meta.deadzone = SAVE_META_DEADZONE_DEFAULT;
SAVE.meta.language = SAVE_META_LANGUAGE_DEFAULT;
#ifdef saveInitPlatform
// A missing/unreachable save medium is expected, recoverable state,
+16
View File
@@ -25,6 +25,13 @@
*/
#define SAVE_META_DEADZONE_DEFAULT 0.1f
/**
* Default language index for meta that's never actually been loaded from
* disk yet - index 0 into LOCALE_LIST (see locale/localemanager.h), i.e.
* LOCALE_EN_US.
*/
#define SAVE_META_LANGUAGE_DEFAULT 0
/**
* Device/user-wide preferences, independent of any individual game save
* slot (see saveslot.h) - there's exactly one of these, not one per slot,
@@ -44,4 +51,13 @@ typedef struct {
* rather than caching it anywhere else.
*/
float_t deadzone;
/**
* Index into LOCALE_LIST (see locale/localemanager.h) for the player's
* preferred UI language - the save meta is the only place this lives;
* read it directly via saveGetMeta()->language rather than caching it
* anywhere else. Never reorder/remove entries from LOCALE_LIST, only
* append, since this index must keep meaning the same locale across
* versions.
*/
uint8_t language;
} savemeta_t;
+2
View File
@@ -340,6 +340,7 @@ errorret_t saveMetaSerializeRead(savestream_t *stream, savemeta_t *meta) {
);
saveFileReadVersion(stream, &meta->version);
saveFileReadFloat(stream, &meta->deadzone);
saveFileReadUInt8(stream, &meta->language);
errorChain(saveStreamVerifyChecksumImpl(stream, "Save meta"));
meta->exists = true;
errorOk();
@@ -354,6 +355,7 @@ errorret_t saveMetaSerializeWrite(savestream_t *stream, savemeta_t *meta) {
saveFileWriteHeader(stream, meta->header, SAVE_META_HEADER_SIZE);
saveFileWriteVersion(stream, &meta->version);
saveFileWriteFloat(stream, &meta->deadzone);
saveFileWriteUInt8(stream, &meta->language);
errorChain(saveStreamFinalizeWriteImpl(
stream, headerPosition, SAVE_META_HEADER_SIZE
));
+59 -10
View File
@@ -33,19 +33,19 @@ static void uiGameMenuSaveWriteComplete(errorret_t result, void *user) {
// the longest platform save-error message plus this prefix.
char_t msg[256];
stringFormat(
msg, sizeof(msg), "Save failed: %s", result.state->message
msg, sizeof(msg), UI_GAME_MENU.saveFailedFormat, result.state->message
);
errorCatch(result);
uiTextboxMainSetText(msg);
return;
}
uiTextboxMainSetText("Game saved.");
uiTextboxMainSetText(UI_GAME_MENU.saveSuccessText);
}
static void uiGameMenuSaveCreateConfirmed(const bool_t confirmed, void *user) {
if(!confirmed) {
uiTextboxMainSetText("Save cancelled.");
uiTextboxMainSetText(UI_GAME_MENU.saveCancelledText);
return;
}
@@ -62,7 +62,10 @@ static void uiGameMenuSaveCreateConfirmed(const bool_t confirmed, void *user) {
static void uiGameMenuSaveCheckComplete(errorret_t result, void *user) {
if(errorIsNotOk(result)) {
char_t msg[256];
stringFormat(msg, sizeof(msg), "Can't save: %s", result.state->message);
stringFormat(
msg, sizeof(msg), UI_GAME_MENU.saveCheckFailedFormat,
result.state->message
);
errorCatch(result);
uiTextboxMainSetText(msg);
return;
@@ -74,7 +77,7 @@ static void uiGameMenuSaveCheckComplete(errorret_t result, void *user) {
}
uiConfirmOpen(
"No save data found. Create a new save?",
UI_GAME_MENU.saveCreateConfirmText,
uiGameMenuSaveCreateConfirmed,
NULL
);
@@ -82,14 +85,11 @@ static void uiGameMenuSaveCheckComplete(errorret_t result, void *user) {
static void uiGameMenuSave(void) {
if(saveIsTemporary()) {
uiTextboxMainSetText(
"This session is temporary - no save device was found, so saving "
"is disabled."
);
uiTextboxMainSetText(UI_GAME_MENU.saveTemporaryText);
return;
}
if(!saveIsAvailable()) {
uiTextboxMainSetText("Can't save - no save device found.");
uiTextboxMainSetText(UI_GAME_MENU.saveUnavailableText);
return;
}
if(saveIsBusy()) return;// A save/load dialog (e.g. on PSP) is already up.
@@ -140,6 +140,55 @@ errorret_t uiGameMenuInit(void) {
UI_GAME_MENU.saveLabel,
UI_GAME_MENU_LABEL_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.game_menu.save_success",
0,
UI_GAME_MENU.saveSuccessText,
UI_GAME_MENU_MESSAGE_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.game_menu.save_cancelled",
0,
UI_GAME_MENU.saveCancelledText,
UI_GAME_MENU_MESSAGE_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.game_menu.save_unavailable",
0,
UI_GAME_MENU.saveUnavailableText,
UI_GAME_MENU_MESSAGE_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.game_menu.save_temporary",
0,
UI_GAME_MENU.saveTemporaryText,
UI_GAME_MENU_MESSAGE_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.game_menu.save_create_confirm",
0,
UI_GAME_MENU.saveCreateConfirmText,
UI_GAME_MENU_MESSAGE_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.game_menu.save_failed_format",
0,
UI_GAME_MENU.saveFailedFormat,
UI_GAME_MENU_MESSAGE_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.game_menu.save_check_failed_format",
0,
UI_GAME_MENU.saveCheckFailedFormat,
UI_GAME_MENU_MESSAGE_MAX
));
MENU_BEGIN(
&UI_GAME_MENU.menu, UI_GAME_MENU.items, uiGameMenuSelected, NULL, NULL
+8
View File
@@ -12,6 +12,7 @@
#define UI_GAME_MENU_ITEM_COUNT 4
#define UI_GAME_MENU_WIDTH 150.0f
#define UI_GAME_MENU_LABEL_MAX 32
#define UI_GAME_MENU_MESSAGE_MAX 192
typedef struct {
uimenu_t menu;
@@ -20,6 +21,13 @@ typedef struct {
char_t itemsLabel[UI_GAME_MENU_LABEL_MAX];
char_t settingsLabel[UI_GAME_MENU_LABEL_MAX];
char_t saveLabel[UI_GAME_MENU_LABEL_MAX];
char_t saveSuccessText[UI_GAME_MENU_MESSAGE_MAX];
char_t saveCancelledText[UI_GAME_MENU_MESSAGE_MAX];
char_t saveUnavailableText[UI_GAME_MENU_MESSAGE_MAX];
char_t saveTemporaryText[UI_GAME_MENU_MESSAGE_MAX];
char_t saveCreateConfirmText[UI_GAME_MENU_MESSAGE_MAX];
char_t saveFailedFormat[UI_GAME_MENU_MESSAGE_MAX];
char_t saveCheckFailedFormat[UI_GAME_MENU_MESSAGE_MAX];
} uigamemenu_t;
extern uigamemenu_t UI_GAME_MENU;
@@ -16,9 +16,10 @@
#include "display/spritebatch/spritebatch.h"
#include "display/texture/texture.h"
#include "display/shader/shaderunlit.h"
#include "locale/localemanager.h"
#include "asset/loader/locale/assetlocaleloader.h"
#define UI_INITIAL_CREATE_SAVE_BACKDROP_COLOR color4b(0, 0, 0, 160)
#define UI_INITIAL_CREATE_SAVE_TEXT "No save data found. Create a new save?"
uiinitialcreatesave_t UI_INITIAL_CREATE_SAVE;
@@ -42,12 +43,34 @@ static void uiInitialCreateSaveClosed(const uimenu_t *menu) {
errorret_t uiInitialCreateSaveInit(void) {
memoryZero(&UI_INITIAL_CREATE_SAVE, sizeof(uiinitialcreatesave_t));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.initial.create_save.message",
0,
UI_INITIAL_CREATE_SAVE.text,
UI_INITIAL_CREATE_SAVE_TEXT_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.initial.create_save.yes",
0,
UI_INITIAL_CREATE_SAVE.yesLabel,
UI_INITIAL_CREATE_SAVE_LABEL_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.initial.create_save.no",
0,
UI_INITIAL_CREATE_SAVE.noLabel,
UI_INITIAL_CREATE_SAVE_LABEL_MAX
));
MENU_BEGIN(
&UI_INITIAL_CREATE_SAVE.menu, UI_INITIAL_CREATE_SAVE.items,
uiInitialCreateSaveSelected, uiInitialCreateSaveClosed, NULL
);
MENU_BUTTON("Yes");
MENU_BUTTON("No");
MENU_BUTTON(UI_INITIAL_CREATE_SAVE.yesLabel);
MENU_BUTTON(UI_INITIAL_CREATE_SAVE.noLabel);
MENU_END(UI_INITIAL_CREATE_SAVE.items, menuIndex);
@@ -75,7 +98,7 @@ errorret_t uiInitialCreateSaveDraw(void) {
errorChain(spriteBatchFlush());
int32_t textW, textH;
textMeasure(UI_INITIAL_CREATE_SAVE_TEXT, &FONT_DEFAULT, &textW, &textH);
textMeasure(UI_INITIAL_CREATE_SAVE.text, &FONT_DEFAULT, &textW, &textH);
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
float_t width = mathMax(
@@ -93,7 +116,7 @@ errorret_t uiInitialCreateSaveDraw(void) {
float_t contentWidth = width - (UI_FRAME_START_X * 2);
errorChain(textDraw(
contentX, contentY, UI_INITIAL_CREATE_SAVE_TEXT, COLOR_WHITE, &FONT_DEFAULT
contentX, contentY, UI_INITIAL_CREATE_SAVE.text, COLOR_WHITE, &FONT_DEFAULT
));
float_t buttonsY = contentY + rowHeight + UI_FRAME_PADDING_Y;
@@ -13,6 +13,8 @@
#define UI_INITIAL_CREATE_SAVE_INDEX_NO 1
#define UI_INITIAL_CREATE_SAVE_ITEM_COUNT 2
#define UI_INITIAL_CREATE_SAVE_MIN_WIDTH 160.0f
#define UI_INITIAL_CREATE_SAVE_TEXT_MAX 256
#define UI_INITIAL_CREATE_SAVE_LABEL_MAX 32
/**
* Callback invoked once the create-save modal is dismissed.
@@ -29,6 +31,9 @@ typedef struct {
uiinitialcreatesavecallback_t callback;
void *user;
bool_t create;
char_t text[UI_INITIAL_CREATE_SAVE_TEXT_MAX];
char_t yesLabel[UI_INITIAL_CREATE_SAVE_LABEL_MAX];
char_t noLabel[UI_INITIAL_CREATE_SAVE_LABEL_MAX];
} uiinitialcreatesave_t;
extern uiinitialcreatesave_t UI_INITIAL_CREATE_SAVE;
+28 -6
View File
@@ -16,10 +16,10 @@
#include "display/spritebatch/spritebatch.h"
#include "display/texture/texture.h"
#include "display/shader/shaderunlit.h"
#include "locale/localemanager.h"
#include "asset/loader/locale/assetlocaleloader.h"
#define UI_INITIAL_NO_CARD_BACKDROP_COLOR color4b(0, 0, 0, 160)
#define UI_INITIAL_NO_CARD_TEXT \
"No save device found. You can continue, but\nprogress will not be saved."
uiinitialnocard_t UI_INITIAL_NO_CARD;
@@ -41,12 +41,34 @@ static void uiInitialNoCardClosed(const uimenu_t *menu) {
errorret_t uiInitialNoCardInit(void) {
memoryZero(&UI_INITIAL_NO_CARD, sizeof(uiinitialnocard_t));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.initial.no_card.message",
0,
UI_INITIAL_NO_CARD.text,
UI_INITIAL_NO_CARD_TEXT_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.initial.no_card.retry",
0,
UI_INITIAL_NO_CARD.retryLabel,
UI_INITIAL_NO_CARD_LABEL_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.initial.no_card.continue",
0,
UI_INITIAL_NO_CARD.continueLabel,
UI_INITIAL_NO_CARD_LABEL_MAX
));
MENU_BEGIN(
&UI_INITIAL_NO_CARD.menu, UI_INITIAL_NO_CARD.items,
uiInitialNoCardSelected, uiInitialNoCardClosed, NULL
);
MENU_BUTTON("Retry");
MENU_BUTTON("Continue Anyway");
MENU_BUTTON(UI_INITIAL_NO_CARD.retryLabel);
MENU_BUTTON(UI_INITIAL_NO_CARD.continueLabel);
MENU_END(UI_INITIAL_NO_CARD.items, menuIndex);
@@ -74,7 +96,7 @@ errorret_t uiInitialNoCardDraw(void) {
errorChain(spriteBatchFlush());
int32_t textW, textH;
textMeasure(UI_INITIAL_NO_CARD_TEXT, &FONT_DEFAULT, &textW, &textH);
textMeasure(UI_INITIAL_NO_CARD.text, &FONT_DEFAULT, &textW, &textH);
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
float_t width = mathMax(
@@ -92,7 +114,7 @@ errorret_t uiInitialNoCardDraw(void) {
float_t contentWidth = width - (UI_FRAME_START_X * 2);
errorChain(textDraw(
contentX, contentY, UI_INITIAL_NO_CARD_TEXT, COLOR_WHITE, &FONT_DEFAULT
contentX, contentY, UI_INITIAL_NO_CARD.text, COLOR_WHITE, &FONT_DEFAULT
));
float_t buttonsY = contentY + (float_t)textH + UI_FRAME_PADDING_Y;
@@ -13,6 +13,8 @@
#define UI_INITIAL_NO_CARD_INDEX_CONTINUE 1
#define UI_INITIAL_NO_CARD_ITEM_COUNT 2
#define UI_INITIAL_NO_CARD_MIN_WIDTH 220.0f
#define UI_INITIAL_NO_CARD_TEXT_MAX 256
#define UI_INITIAL_NO_CARD_LABEL_MAX 32
/**
* Callback invoked once the no-save-device modal is dismissed.
@@ -28,6 +30,9 @@ typedef struct {
uiinitialnocardcallback_t callback;
void *user;
bool_t retry;
char_t text[UI_INITIAL_NO_CARD_TEXT_MAX];
char_t retryLabel[UI_INITIAL_NO_CARD_LABEL_MAX];
char_t continueLabel[UI_INITIAL_NO_CARD_LABEL_MAX];
} uiinitialnocard_t;
extern uiinitialnocard_t UI_INITIAL_NO_CARD;
+3 -3
View File
@@ -8,19 +8,19 @@
#pragma once
#include "error/error.h"
#include "ui/widget/uimenu.h"
#include "locale/localemanager.h"
#define UI_SETTINGS_GENERAL_ITEM_COUNT 3
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE 0
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE_DETAIL 1
#define UI_SETTINGS_GENERAL_INDEX_APPLY 2
#define UI_SETTINGS_GENERAL_LOCALE_COUNT 3
#define UI_SETTINGS_GENERAL_LABEL_MAX 32
#define UI_SETTINGS_GENERAL_INFO_MAX 64
#define UI_SETTINGS_GENERAL_INFO_MAX 96
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_GENERAL_ITEM_COUNT];
const char_t *localeNames[UI_SETTINGS_GENERAL_LOCALE_COUNT];
const char_t *localeNames[LOCALE_LIST_COUNT];
char_t languageLabel[UI_SETTINGS_GENERAL_LABEL_MAX];
char_t labelInfo[UI_SETTINGS_GENERAL_INFO_MAX];
} uisettingsgeneral_t;
+14 -23
View File
@@ -9,18 +9,15 @@
#include "uisettings.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "locale/localemanager.h"
#include "locale/localeinfo.h"
#include "asset/loader/locale/assetlocaleloader.h"
#include "save/save.h"
#include "error/error.h"
static const localeinfo_t *const UI_SETTINGS_GENERAL_LOCALES[
UI_SETTINGS_GENERAL_LOCALE_COUNT
] = {
&LOCALE_EN_US,
&LOCALE_JP_JP,
&LOCALE_ES_MX
};
static void uiSettingsGeneralSaveComplete(errorret_t result, void *user) {
if(errorIsNotOk(result)) errorCatch(errorPrint(result));
}
void uiSettingsGeneralSelected(
const uimenu_t *menu,
@@ -41,8 +38,8 @@ errorret_t uiSettingsGeneralInit(uisettingsdata_t *data) {
uisettingsgeneral_t *general = &data->general;
memoryZero(general, sizeof(uisettingsgeneral_t));
for(uint8_t i = 0; i < UI_SETTINGS_GENERAL_LOCALE_COUNT; i++) {
general->localeNames[i] = UI_SETTINGS_GENERAL_LOCALES[i]->name;
for(uint8_t i = 0; i < LOCALE_LIST_COUNT; i++) {
general->localeNames[i] = LOCALE_LIST[i]->name;
}
errorChain(assetLocaleGetString(
@@ -67,7 +64,7 @@ errorret_t uiSettingsGeneralInit(uisettingsdata_t *data) {
MENU_DROPDOWN(
general->languageLabel, general->localeNames,
UI_SETTINGS_GENERAL_LOCALE_COUNT, 0
LOCALE_LIST_COUNT, 0
);
MENU_LABEL(general->labelInfo);
@@ -80,14 +77,7 @@ errorret_t uiSettingsGeneralInit(uisettingsdata_t *data) {
void uiSettingsGeneralLoad(void) {
uisettingsgeneral_t *general = &UI_SETTINGS.data.general;
uint8_t index = 0;
for(uint8_t i = 0; i < UI_SETTINGS_GENERAL_LOCALE_COUNT; i++) {
if(stringCompare(LOCALE.locale->file, UI_SETTINGS_GENERAL_LOCALES[i]->file) != 0) {
continue;
}
index = i;
break;
}
uint8_t index = localeManagerGetIndex(LOCALE.locale);
uiDropdownSetSelectedIndex(
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown, index
@@ -100,7 +90,10 @@ void uiSettingsGeneralApply(void) {
uint8_t index = uiDropdownGetSelectedIndex(
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
);
errorCatch(localeManagerSetLocale(UI_SETTINGS_GENERAL_LOCALES[index]));
errorCatch(localeManagerSetLocale(LOCALE_LIST[index]));
saveGetMeta()->language = index;
saveWriteMeta(uiSettingsGeneralSaveComplete, NULL);
uiMenuClose(&general->menu);
}
@@ -111,7 +104,5 @@ bool_t uiSettingsGeneralHasChanges(void) {
uint8_t index = uiDropdownGetSelectedIndex(
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
);
return stringCompare(
LOCALE.locale->file, UI_SETTINGS_GENERAL_LOCALES[index]->file
) != 0;
return localeManagerGetIndex(LOCALE.locale) != index;
}
+14
View File
@@ -31,6 +31,12 @@ void saveJsonWriterAddFloatLinux(
yyjson_mut_obj_add_real(writer->doc, writer->root, key, (double)value);
}
void saveJsonWriterAddUInt8Linux(
savejsonwriterlinux_t *writer, const char_t *key, const uint8_t value
) {
yyjson_mut_obj_add_uint(writer->doc, writer->root, key, (uint64_t)value);
}
void saveJsonWriterAddStringLinux(
savejsonwriterlinux_t *writer, const char_t *key, const char_t *value
) {
@@ -122,6 +128,14 @@ float_t saveJsonReadFloatLinux(
return (float_t)yyjson_get_num(val);
}
uint8_t saveJsonReadUInt8Linux(
yyjson_val *root, const char_t *key, const uint8_t defaultValue
) {
yyjson_val *val = yyjson_obj_get(root, key);
if(!val || !yyjson_is_num(val)) return defaultValue;
return (uint8_t)yyjson_get_uint(val);
}
void saveJsonReadStringLinux(
yyjson_val *root, const char_t *key, char_t *out, const size_t maxLen,
const char_t *defaultValue
+15
View File
@@ -42,6 +42,13 @@ void saveJsonWriterAddFloatLinux(
savejsonwriterlinux_t *writer, const char_t *key, const float_t value
);
/**
* Adds a "key": <uint> field to the root object.
*/
void saveJsonWriterAddUInt8Linux(
savejsonwriterlinux_t *writer, const char_t *key, const uint8_t value
);
/**
* Adds a string field to the root object. The value is copied into the
* document, so the caller's buffer doesn't need to outlive the call.
@@ -120,6 +127,14 @@ float_t saveJsonReadFloatLinux(
yyjson_val *root, const char_t *key, const float_t defaultValue
);
/**
* Reads a uint8 field, falling back to defaultValue if the key is
* missing or not a number.
*/
uint8_t saveJsonReadUInt8Linux(
yyjson_val *root, const char_t *key, const uint8_t defaultValue
);
/**
* Reads a string field into out, falling back to defaultValue if the key
* is missing or not a string. Always null-terminates.
+4
View File
@@ -120,6 +120,9 @@ errorret_t saveMetaLoadLinux(savemeta_t *out) {
out->deadzone = saveJsonReadFloatLinux(
root, "deadzone", SAVE_META_DEADZONE_DEFAULT
);
out->language = saveJsonReadUInt8Linux(
root, "language", SAVE_META_LANGUAGE_DEFAULT
);
out->exists = true;
yyjson_doc_free(doc);
@@ -136,6 +139,7 @@ errorret_t saveMetaWriteLinux(savemeta_t *meta) {
errorChain(saveJsonWriterInitLinux(&writer));
saveJsonWriterAddUInt32Linux(&writer, "version", meta->version);
saveJsonWriterAddFloatLinux(&writer, "deadzone", meta->deadzone);
saveJsonWriterAddUInt8Linux(&writer, "language", meta->language);
errorret_t ret = saveJsonWriterSaveLinux(&writer, path);
saveJsonWriterDisposeLinux(&writer);