From fc5bb119ce2fe48d26563324a5f215c6727a0096 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:50:31 +1100 Subject: [PATCH] French UI batch 3: Notes, Reader, Audiobooks, Voice messages, Alarm, Map, Games (menu, Snake, Minesweeper, Game Boy) --- examples/companion_radio/ui-new/AlarmScreen.h | 76 ++++++++++--------- .../ui-new/AudiobookPlayerScreen.h | 53 ++++++------- .../ui-new/GBCEmulatorScreen.cpp | 38 +++++----- .../ui-new/GBCEmulatorScreen.h | 1 + .../companion_radio/ui-new/GamesMenuScreen.h | 14 ++-- examples/companion_radio/ui-new/MapScreen.h | 7 +- .../ui-new/MinesweeperScreen.h | 23 +++--- examples/companion_radio/ui-new/NotesScreen.h | 61 +++++++-------- examples/companion_radio/ui-new/SnakeScreen.h | 31 ++++---- .../companion_radio/ui-new/TextReaderScreen.h | 47 ++++++------ .../ui-new/VoiceMessageScreen.h | 63 +++++++-------- 11 files changed, 214 insertions(+), 200 deletions(-) diff --git a/examples/companion_radio/ui-new/AlarmScreen.h b/examples/companion_radio/ui-new/AlarmScreen.h index 13390d5f..91b6e3b0 100644 --- a/examples/companion_radio/ui-new/AlarmScreen.h +++ b/examples/companion_radio/ui-new/AlarmScreen.h @@ -27,6 +27,7 @@ #include #include +#include "MeckLang.h" #include #include @@ -199,7 +200,8 @@ private: static const char* dowShort(int dow) { static const char* names[] = {"Su","Mo","Tu","We","Th","Fr","Sa"}; - return (dow >= 0 && dow < 7) ? names[dow] : "??"; + static const char* names_fr[] = {"Di","Lu","Ma","Me","Je","Ve","Sa"}; + return (dow >= 0 && dow < 7) ? MECK_TR(names[dow], names_fr[dow]) : "??"; } // Day of week from epoch (0=Sunday) @@ -210,10 +212,10 @@ private: // Format days bitmask as human string static void formatDays(uint8_t days, char* buf, int bufLen) { - if (days == DOW_ALL) { strncpy(buf, "Every day", bufLen); return; } - if (days == DOW_WEEKDAYS) { strncpy(buf, "Weekdays", bufLen); return; } - if (days == DOW_WEEKEND) { strncpy(buf, "Weekend", bufLen); return; } - if (days == 0) { strncpy(buf, "Never", bufLen); return; } + if (days == DOW_ALL) { strncpy(buf, MECK_TR("Every day", "Tous les jours"), bufLen); return; } + if (days == DOW_WEEKDAYS) { strncpy(buf, MECK_TR("Weekdays", "En semaine"), bufLen); return; } + if (days == DOW_WEEKEND) { strncpy(buf, MECK_TR("Weekend", "Week-end"), bufLen); return; } + if (days == 0) { strncpy(buf, MECK_TR("Never", "Jamais"), bufLen); return; } buf[0] = '\0'; for (int i = 0; i < 7; i++) { if (days & (1 << i)) { @@ -451,14 +453,14 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::GREEN); display.setCursor(0, 0); - display.print("Alarm Clock"); + display.print(MECK_TR("Alarm Clock", "R\xC3\xA9veil")); if (_soundFiles.empty() && _sdReady) { // Show hint if no sounds yet display.setColor(DisplayDriver::LIGHT); display.setTextSize(0); display.setCursor(0, 13); - display.print("Place 44kHz .mp3 in /alarms/"); + display.print(MECK_TR("Place 44kHz .mp3 in /alarms/", ".mp3 (44 kHz) dans /alarms/")); } display.setColor(DisplayDriver::LIGHT); @@ -495,7 +497,7 @@ private: // Line 1: Alarm number + time + enabled char line1[40]; snprintf(line1, sizeof(line1), "%d. %s %02d:%02d", - idx + 1, slot.enabled ? "ON " : "OFF", slot.hour, slot.minute); + idx + 1, slot.enabled ? MECK_TR("ON ", "OUI") : MECK_TR("OFF", "NON"), slot.hour, slot.minute); display.setCursor(0, y); display.print(line1); @@ -524,7 +526,7 @@ private: display.print(line2); } - drawFooter(display, "O:On/Off Enter:Edit", "Q:X"); + drawFooter(display, MECK_TR("O:On/Off Enter:Edit", "O:Oui/Non Ent:\xC3\x89" "diter"), "Q:X"); } // ---- Render: Edit alarm ---- @@ -534,7 +536,7 @@ private: display.setColor(DisplayDriver::GREEN); display.setCursor(0, 0); char hdr[24]; - snprintf(hdr, sizeof(hdr), "Edit Alarm %d", _editSlot + 1); + snprintf(hdr, sizeof(hdr), MECK_TR("Edit Alarm %d", "Modifier le r\xC3\xA9veil %d"), _editSlot + 1); display.print(hdr); display.setColor(DisplayDriver::LIGHT); @@ -550,11 +552,11 @@ private: char value[32]; } fields[FIELD_COUNT]; - snprintf(fields[FIELD_ENABLED].value, 32, "%s", _editCopy.enabled ? "ON" : "OFF"); - fields[FIELD_ENABLED].label = "Enabled"; + snprintf(fields[FIELD_ENABLED].value, 32, "%s", _editCopy.enabled ? MECK_TR("ON", "OUI") : MECK_TR("OFF", "NON")); + fields[FIELD_ENABLED].label = MECK_TR("Enabled", "Activ\xC3\xA9"); snprintf(fields[FIELD_HOUR].value, 32, "%02d", _editCopy.hour); - fields[FIELD_HOUR].label = "Hour"; + fields[FIELD_HOUR].label = MECK_TR("Hour", "Heure"); snprintf(fields[FIELD_MINUTE].value, 32, "%02d", _editCopy.minute); fields[FIELD_MINUTE].label = "Minute"; @@ -563,14 +565,14 @@ private: formatDays(_editCopy.days, daysBuf, sizeof(daysBuf)); strncpy(fields[FIELD_DAYS].value, daysBuf, 31); fields[FIELD_DAYS].value[31] = '\0'; - fields[FIELD_DAYS].label = "Days"; + fields[FIELD_DAYS].label = MECK_TR("Days", "Jours"); snprintf(fields[FIELD_VOLUME].value, 32, "%d", _editCopy.volume); fields[FIELD_VOLUME].label = "Volume"; #if defined(LilyGo_TDeck_Pro_Max) if (slotIsVibrate(_editCopy)) { - strncpy(fields[FIELD_SOUND].value, "Buzzer (vibrate)", 31); + strncpy(fields[FIELD_SOUND].value, MECK_TR("Buzzer (vibrate)", "Buzzer (vibreur)"), 31); fields[FIELD_SOUND].value[31] = '\0'; } else #endif @@ -582,9 +584,9 @@ private: if (dot) *dot = '\0'; strncpy(fields[FIELD_SOUND].value, sndDisplay, 31); } else { - strcpy(fields[FIELD_SOUND].value, "(default)"); + strcpy(fields[FIELD_SOUND].value, MECK_TR("(default)", "(d\xC3\xA9" "faut)")); } - fields[FIELD_SOUND].label = "Sound"; + fields[FIELD_SOUND].label = MECK_TR("Sound", "Son"); for (int f = 0; f < FIELD_COUNT; f++) { int fy = y + f * lineH; @@ -622,7 +624,7 @@ private: int detailY = y + FIELD_COUNT * lineH + 4; display.setColor(DisplayDriver::GREEN); display.setCursor(0, detailY); - display.print("A/D: toggle day "); + display.print(MECK_TR("A/D: toggle day ", "A/D : jour oui/non ")); // Show individual day toggles display.setCursor(0, detailY + 9); for (int d = 0; d < 7; d++) { @@ -644,7 +646,7 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::GREEN); - const char* prompt = (_editField == FIELD_HOUR) ? "Hour (0-23):" : "Min (0-59):"; + const char* prompt = (_editField == FIELD_HOUR) ? MECK_TR("Hour (0-23):", "Heure (0-23) :") : MECK_TR("Min (0-59):", "Min (0-59) :"); display.setCursor(bx + 4, by + 4); display.print(prompt); @@ -655,9 +657,9 @@ private: display.setCursor(bx + 4, by + 16); display.print(inputDisplay); - drawFooter(display, "Type digits", "Enter:OK Sh+Del:Cancel"); + drawFooter(display, MECK_TR("Type digits", "Chiffres"), MECK_TR("Enter:OK Sh+Del:Cancel", "Ent:OK Sh+Del:Annuler")); } else { - drawFooter(display, "A/D:Adjust Enter:Type", "Q:Save"); + drawFooter(display, MECK_TR("A/D:Adjust Enter:Type", "A/D:R\xC3\xA9gler Ent:Saisie"), MECK_TR("Q:Save", "Q:OK")); } } @@ -667,19 +669,19 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::GREEN); display.setCursor(0, 0); - display.print("Pick Alarm Sound"); + display.print(MECK_TR("Pick Alarm Sound", "Choisir le son du r\xC3\xA9veil")); display.setColor(DisplayDriver::LIGHT); if (_soundFiles.empty() && kVibrateRows == 0) { display.setTextSize(0); display.setCursor(0, 20); - display.print("No .mp3 files found."); + display.print(MECK_TR("No .mp3 files found.", "Aucun fichier .mp3 trouv\xC3\xA9.")); display.setCursor(0, 30); - display.print("Place 44kHz .mp3 in"); + display.print(MECK_TR("Place 44kHz .mp3 in", "Placez des .mp3 44 kHz dans")); display.setCursor(0, 38); - display.print("/alarms/ on SD card"); - drawFooter(display, "0 files", "Q:Back"); + display.print(MECK_TR("/alarms/ on SD card", "/alarms/ sur la carte SD")); + drawFooter(display, MECK_TR("0 files", "0 fichier"), MECK_TR("Q:Back", "Q:Retour")); return; } @@ -711,7 +713,7 @@ private: String displayName; if (kVibrateRows && idx == 0) { - displayName = "Buzzer (vibrate)"; + displayName = MECK_TR("Buzzer (vibrate)", "Buzzer (vibreur)"); } else { // Display filename without extension displayName = _soundFiles[idx - kVibrateRows]; @@ -726,8 +728,8 @@ private: } char countBuf[12]; - snprintf(countBuf, sizeof(countBuf), "%d files", (int)_soundFiles.size()); - drawFooter(display, countBuf, "Enter:Pick Q:X"); + snprintf(countBuf, sizeof(countBuf), MECK_TR("%d files", "%d fichiers"), (int)_soundFiles.size()); + drawFooter(display, countBuf, MECK_TR("Enter:Pick Q:X", "Ent:Choisir Q:X")); } // ---- Render: Ringing ---- @@ -746,14 +748,14 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); char label[24]; - snprintf(label, sizeof(label), "Alarm %d", _ringingSlot + 1); + snprintf(label, sizeof(label), MECK_TR("Alarm %d", "R\xC3\xA9veil %d"), _ringingSlot + 1); display.drawTextCentered(display.width() / 2, 34, label); // Sound name #if defined(LilyGo_TDeck_Pro_Max) if (slotIsVibrate(slot)) { display.setTextSize(0); - display.drawTextCentered(display.width() / 2, 48, "Buzzer (vibrate)"); + display.drawTextCentered(display.width() / 2, 48, MECK_TR("Buzzer (vibrate)", "Buzzer (vibreur)")); } else #endif if (slot.sound[0] != '\0') { @@ -771,13 +773,13 @@ private: display.setTextSize(1); if (meck_alarm_is_locked()) { // Keys are ignored while locked; the user unlocks with the button first. - display.drawTextCentered(display.width() / 2, 64, "Unlock to dismiss"); + display.drawTextCentered(display.width() / 2, 64, MECK_TR("Unlock to dismiss", "D\xC3\xA9verrouillez pour arr\xC3\xAAter")); } else { - display.drawTextCentered(display.width() / 2, 64, "ANY KEY: Dismiss"); + display.drawTextCentered(display.width() / 2, 64, MECK_TR("ANY KEY: Dismiss", "UNE TOUCHE : arr\xC3\xAAter")); display.setTextSize(0); display.setColor(DisplayDriver::LIGHT); - display.drawTextCentered(display.width() / 2, 80, "Z: Snooze 5 min"); + display.drawTextCentered(display.width() / 2, 80, MECK_TR("Z: Snooze 5 min", "Z : rappel dans 5 min")); } // No footer in ringing mode — keep it clean and urgent @@ -1224,11 +1226,11 @@ public: display.setTextSize(1); display.setColor(DisplayDriver::RED); display.setCursor(0, 20); - display.print("No SD card"); + display.print(MECK_TR("No SD card", "Pas de carte SD")); display.setCursor(0, 35); - display.print("Insert SD card and"); + display.print(MECK_TR("Insert SD card and", "Ins\xC3\xA9rez une carte SD et")); display.setCursor(0, 43); - display.print("create /alarms/"); + display.print(MECK_TR("create /alarms/", "cr\xC3\xA9" "ez /alarms/")); return 5000; } diff --git a/examples/companion_radio/ui-new/AudiobookPlayerScreen.h b/examples/companion_radio/ui-new/AudiobookPlayerScreen.h index ef196ae3..c7851066 100644 --- a/examples/companion_radio/ui-new/AudiobookPlayerScreen.h +++ b/examples/companion_radio/ui-new/AudiobookPlayerScreen.h @@ -29,6 +29,7 @@ #include #include +#include "MeckLang.h" #include #include #include @@ -501,13 +502,13 @@ private: _displayRef->setTextSize(2); _displayRef->setColor(DisplayDriver::GREEN); _displayRef->setCursor(10, 15); - _displayRef->print("Loading"); + _displayRef->print(MECK_TR("Loading", "Chargement")); _displayRef->setCursor(10, 30); - _displayRef->print("Audiobooks"); + _displayRef->print(MECK_TR("Audiobooks", "Livres audio")); _displayRef->setTextSize(1); _displayRef->setColor(DisplayDriver::LIGHT); _displayRef->setCursor(10, 55); - _displayRef->print("Please wait..."); + _displayRef->print(MECK_TR("Please wait...", "Veuillez patienter...")); _displayRef->endFrame(); } @@ -981,7 +982,7 @@ private: display->setTextSize(1); display->setColor(DisplayDriver::GREEN); display->setCursor(10, 11); - display->print("Loading..."); + display->print(MECK_TR("Loading...", "Chargement...")); display->setTextSize(1); display->setColor(DisplayDriver::LIGHT); display->setCursor(10, 30); @@ -1267,19 +1268,19 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::GREEN); display.setCursor(0, 0); - display.print("Audiobooks"); + display.print(MECK_TR("Audiobooks", "Livres audio")); display.setColor(DisplayDriver::LIGHT); if (_fileList.size() == 0) { display.setCursor(0, 20); - display.print("No audiobooks found."); + display.print(MECK_TR("No audiobooks found.", "Aucun livre audio trouv\xC3\xA9.")); display.setCursor(0, 30); - display.print("Place .m4b/.mp3 in"); + display.print(MECK_TR("Place .m4b/.mp3 in", "Placez des .m4b/.mp3 dans")); display.setCursor(0, 38); - display.print("/audiobooks/ on SD"); + display.print(MECK_TR("/audiobooks/ on SD", "/audiobooks/ sur la SD")); - drawFooter(display, "0 files", "Q:Back"); + drawFooter(display, MECK_TR("0 files", "0 fichier"), MECK_TR("Q:Back", "Q:Retour")); return; } @@ -1321,7 +1322,7 @@ private: if (fe.isDir) { if (fe.name == "..") { - snprintf(fullLine, sizeof(fullLine), ".. (up)"); + snprintf(fullLine, sizeof(fullLine), MECK_TR(".. (up)", ".. (parent)")); } else { snprintf(fullLine, sizeof(fullLine), "/%s", fe.name.c_str()); } @@ -1366,8 +1367,8 @@ private: // Left label is always the file count — the folder path was overflowing and // colliding with the right-hand nav text, so it is no longer shown here. char leftBuf[32]; - snprintf(leftBuf, sizeof(leftBuf), "%d files", (int)_fileList.size()); - drawFooter(display, leftBuf, "W/S:Nav Enter:Open"); + snprintf(leftBuf, sizeof(leftBuf), MECK_TR("%d files", "%d fichiers"), (int)_fileList.size()); + drawFooter(display, leftBuf, MECK_TR("W/S:Nav Enter:Open", "W/S Ent:Ouvrir")); } // ---- Render: Player ---- @@ -1413,7 +1414,7 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); char chBuf[24]; - snprintf(chBuf, sizeof(chBuf), "Ch %d/%d", + snprintf(chBuf, sizeof(chBuf), MECK_TR("Ch %d/%d", "Chap. %d/%d"), _currentChapter + 1, _metadata.chapterCount); display.drawTextCentered(display.width() / 2, y, chBuf); y += 10; @@ -1423,7 +1424,7 @@ private: { display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); - const char* stateStr = _isPlaying ? (_isPaused ? "Paused" : "Playing") : "Stopped"; + const char* stateStr = _isPlaying ? (_isPaused ? MECK_TR("Paused", "En pause") : MECK_TR("Playing", "Lecture")) : MECK_TR("Stopped", "Arr\xC3\xAAt\xC3\xA9"); char stateBuf[24]; snprintf(stateBuf, sizeof(stateBuf), "%s Vol:%d", stateStr, _volume); display.drawTextCentered(display.width() / 2, y, stateBuf); @@ -1468,13 +1469,13 @@ private: // Show track position in playlist (if multiple tracks) if (_playlist.size() > 1 && _playlistIdx >= 0) { char trackBuf[24]; - snprintf(trackBuf, sizeof(trackBuf), "Track %d/%d", + snprintf(trackBuf, sizeof(trackBuf), MECK_TR("Track %d/%d", "Piste %d/%d"), _playlistIdx + 1, (int)_playlist.size()); display.drawTextCentered(display.width() / 2, y, trackBuf); y += 10; } - display.drawTextCentered(display.width() / 2, y, "Enter: Play/Pause"); + display.drawTextCentered(display.width() / 2, y, MECK_TR("Enter: Play/Pause", "Entr\xC3\xA9" "e : lecture/pause")); y += 10; // Sleep timer or additional hints @@ -1487,17 +1488,17 @@ private: int mins = remaining / 60; int secs = remaining % 60; char sleepBuf[32]; - snprintf(sleepBuf, sizeof(sleepBuf), "Sleep: %d:%02d (Z:Off)", mins, secs); + snprintf(sleepBuf, sizeof(sleepBuf), MECK_TR("Sleep: %d:%02d (Z:Off)", "Veille : %d:%02d (Z:arr\xC3\xAAt)"), mins, secs); display.drawTextCentered(display.width() / 2, y, sleepBuf); } else if (_isPlaying && !_isPaused) { display.drawTextCentered(display.width() / 2, y, - "Z: Start 45m sleep timer"); + MECK_TR("Z: Start 45m sleep timer", "Z : minuterie 45 min")); } else if (_metadata.chapterCount > 0) { display.drawTextCentered(display.width() / 2, y, - "[/]: Prev/Next Chapter"); + MECK_TR("[/]: Prev/Next Chapter", "[/] : chapitre pr\xC3\xA9" "c./suiv.")); } else if (_playlist.size() > 1) { display.drawTextCentered(display.width() / 2, y, - "N: Next Track"); + MECK_TR("N: Next Track", "N : piste suivante")); } } } @@ -1505,11 +1506,11 @@ private: // ---- Footer Nav Bar ---- { - const char* rightText = (_isPlaying && !_isPaused) ? "Q:Leave" : "Q:Close"; + const char* rightText = (_isPlaying && !_isPaused) ? MECK_TR("Q:Leave", "Q:Sortir") : MECK_TR("Q:Close", "Q:Fermer"); if (_playlist.size() > 1) { - drawFooter(display, "A/D:Seek N:Next", rightText); + drawFooter(display, MECK_TR("A/D:Seek N:Next", "A/D:Saut N:Suiv."), rightText); } else { - drawFooter(display, "A/D:Seek W/S:Vol", rightText); + drawFooter(display, MECK_TR("A/D:Seek W/S:Vol", "A/D:Saut W/S:Vol"), rightText); } } } @@ -1677,11 +1678,11 @@ public: display.setCursor(0, 20); display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); - display.print("SD card not found"); + display.print(MECK_TR("SD card not found", "Carte SD introuvable")); display.setCursor(0, 35); - display.print("Insert SD with"); + display.print(MECK_TR("Insert SD with", "Ins\xC3\xA9rez une SD avec")); display.setCursor(0, 43); - display.print("/audiobooks/ folder"); + display.print(MECK_TR("/audiobooks/ folder", "le dossier /audiobooks/")); return 5000; } diff --git a/examples/companion_radio/ui-new/GBCEmulatorScreen.cpp b/examples/companion_radio/ui-new/GBCEmulatorScreen.cpp index da70b358..ae2bd696 100644 --- a/examples/companion_radio/ui-new/GBCEmulatorScreen.cpp +++ b/examples/companion_radio/ui-new/GBCEmulatorScreen.cpp @@ -609,7 +609,7 @@ bool GBCEmulatorScreen::launch(int idx) { if (!gbc_alloc_buffers()) { gbc_release_core(); - setStatus("Out of memory"); + setStatus(MECK_TR("Out of memory", "M\xC3\xA9moire insuffisante")); return false; } @@ -618,7 +618,7 @@ bool GBCEmulatorScreen::launch(int idx) { if (!f) { Serial.printf("[GBC] cannot open %s\n", path); gbc_release_core(); - setStatus("Cannot open ROM"); + setStatus(MECK_TR("Cannot open ROM", "ROM illisible")); return false; } const size_t sz = (size_t)f.size(); @@ -626,7 +626,7 @@ bool GBCEmulatorScreen::launch(int idx) { f.close(); Serial.println("[GBC] file too small to be a ROM"); gbc_release_core(); - setStatus("Not a ROM"); + setStatus(MECK_TR("Not a ROM", "Pas une ROM")); return false; } if (s_rom && s_rom_cap < sz) { @@ -646,7 +646,7 @@ bool GBCEmulatorScreen::launch(int idx) { Serial.printf("[GBC] ROM alloc failed (%u bytes; largest free PSRAM block %u)\n", (unsigned)sz, (unsigned)heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM)); gbc_release_core(); - setStatus("Out of PSRAM"); + setStatus(MECK_TR("Out of PSRAM", "PSRAM insuffisante")); return false; } s_rom_cap = want; @@ -657,7 +657,7 @@ bool GBCEmulatorScreen::launch(int idx) { if (got != sz) { Serial.printf("[GBC] short read (%u/%u)\n", (unsigned)got, (unsigned)sz); gbc_release_core(); - setStatus("ROM read failed"); + setStatus(MECK_TR("ROM read failed", "\xC3\x89" "chec de lecture ROM")); return false; } s_rom_size = sz; @@ -681,7 +681,7 @@ bool GBCEmulatorScreen::launch(int idx) { Serial.printf("[GBC] gb_init failed: %d\n", (int)e); s_rom_size = 0; gbc_release_core(); - setStatus("Unsupported ROM"); + setStatus(MECK_TR("Unsupported ROM", "ROM non prise en charge")); return false; } if (s_gb->mbc == 2 && s_save_size == 0) s_save_size = 512; @@ -782,7 +782,7 @@ bool GBCEmulatorScreen::launch(int idx) { otaResumeRadio(); #endif gbc_release_core(); - setStatus("Task create failed"); + setStatus(MECK_TR("Task create failed", "\xC3\x89" "chec de d\xC3\xA9marrage")); return false; } _playing = idx; @@ -822,8 +822,8 @@ void GBCEmulatorScreen::finishStop() { gbc_release_core(); _playing = -1; _mode = BROWSER; - if (s_core_error) setStatus("Game crashed"); - else if (saved) setStatus("Saved"); + if (s_core_error) setStatus(MECK_TR("Game crashed", "Le jeu a plant\xC3\xA9")); + else if (saved) setStatus(MECK_TR("Saved", "Sauvegard\xC3\xA9")); print_heaps("after stop"); if (_eink) { _eink->setFastWaveform(true); @@ -913,14 +913,14 @@ bool GBCEmulatorScreen::handleInput(char c) { if (_cursor < _romCount - 1) _cursor++; return true; case '\r': - if (_romCount == 0) { setStatus("No ROMs in /roms"); return true; } + if (_romCount == 0) { setStatus(MECK_TR("No ROMs in /roms", "Aucune ROM dans /roms")); return true; } // Reading a 2 MB ROM off the SD card takes several seconds. Show the // standard alert box first and launch from poll() once it has been // drawn, so the panel is not left showing the ROM list meanwhile. _pendingLaunch = _cursor; _loadingDrawn = false; _mode = LOADING; - _task->showAlert("Loading...", 30000); + _task->showAlert(MECK_TR("Loading...", "Chargement..."), 30000); return true; case KEY_CANCEL: _wantsExit = true; @@ -942,7 +942,7 @@ static void draw_game_header(DisplayDriver& display, UITask* task) { NodePrefs* prefs = task->getNodePrefs(); const int hdrY = -3; char label[16]; - snprintf(label, sizeof(label), "%d Unread", task->getUnreadMsgCount()); + snprintf(label, sizeof(label), MECK_TR("%d Unread", "%d non lus"), task->getUnreadMsgCount()); display.setTextSize(prefs->smallTextSize()); display.setColor(DisplayDriver::GREEN); @@ -1022,8 +1022,8 @@ int GBCEmulatorScreen::renderBrowser(DisplayDriver& display) { display.setTextSize(prefs->smallTextSize()); if (_romCount == 0) { display.setColor(DisplayDriver::LIGHT); - display.drawTextEllipsized(6, headerH, display.width() - 12, "No .gb/.gbc files"); - display.drawTextEllipsized(6, headerH + lineH, display.width() - 12, "in /roms on SD"); + display.drawTextEllipsized(6, headerH, display.width() - 12, MECK_TR("No .gb/.gbc files", "Aucun fichier .gb/.gbc")); + display.drawTextEllipsized(6, headerH + lineH, display.width() - 12, MECK_TR("in /roms on SD", "dans /roms sur la SD")); } else { int y = headerH; for (int i = _scroll; i < _romCount && i < _scroll + rows; i++) { @@ -1051,7 +1051,7 @@ int GBCEmulatorScreen::renderBrowser(DisplayDriver& display) { display.print(_status); return 500; } - display.print("Enter:Play Q:Back"); + display.print(MECK_TR("Enter:Play Q:Back", "Ent:Jouer Q:Retour")); return 5000; } @@ -1075,11 +1075,11 @@ int GBCEmulatorScreen::renderGame(DisplayDriver& display) { const uint16_t fg = d.rawFgColor(); d.drawXbmRaw(GBC_IMG_X, GBC_IMG_Y, s_mono_ui, OUT_W, OUT_H, fg); #if GBC_SOUND - d.drawTextRaw(2, 308, (_mode == STOPPING) ? "Saving..." - : (!s_audio_on ? "Q: Quit" - : (s_muted ? "Q: Quit Press Mic key to unmute" : "Q: Quit Mic: Mute")), fg); + d.drawTextRaw(2, 308, (_mode == STOPPING) ? MECK_TR("Saving...", "Sauvegarde...") + : (!s_audio_on ? MECK_TR("Q: Quit", "Q: Quitter") + : (s_muted ? MECK_TR("Q: Quit Press Mic key to unmute", "Q: Quitter Mic: remettre le son") : MECK_TR("Q: Quit Mic: Mute", "Q: Quitter Mic: Muet"))), fg); #else - d.drawTextRaw(2, 308, (_mode == STOPPING) ? "Saving..." : "Q: Quit", fg); + d.drawTextRaw(2, 308, (_mode == STOPPING) ? MECK_TR("Saving...", "Sauvegarde...") : MECK_TR("Q: Quit", "Q: Quitter"), fg); #endif // No windowed refresh: it was measured to save nothing (the panel's // waveform time is the same for any window), and it left the footer row diff --git a/examples/companion_radio/ui-new/GBCEmulatorScreen.h b/examples/companion_radio/ui-new/GBCEmulatorScreen.h index 81211e24..7b68173c 100644 --- a/examples/companion_radio/ui-new/GBCEmulatorScreen.h +++ b/examples/companion_radio/ui-new/GBCEmulatorScreen.h @@ -22,6 +22,7 @@ #include #include +#include "MeckLang.h" class UITask; class GxEPDDisplay; diff --git a/examples/companion_radio/ui-new/GamesMenuScreen.h b/examples/companion_radio/ui-new/GamesMenuScreen.h index a38d6825..1010581a 100644 --- a/examples/companion_radio/ui-new/GamesMenuScreen.h +++ b/examples/companion_radio/ui-new/GamesMenuScreen.h @@ -10,6 +10,7 @@ #include #include +#include "MeckLang.h" // Forward declarations class UITask; @@ -39,6 +40,7 @@ private: GameID id; const char* name; const char* description; + const char* name_fr; // French name (Experimental Features > Language) }; // The Game Boy emulator entry is present on the T-Deck Pro and Max, except on @@ -51,10 +53,10 @@ private: static const GameEntry* getGames() { static const GameEntry games[NUM_GAMES] = { - { GAME_SNAKE, "Snake", "Classic Nokia-style" }, - { GAME_MINESWEEPER, "Minesweeper", "Find the mines" }, + { GAME_SNAKE, "Snake", "Classic Nokia-style", "Snake" }, + { GAME_MINESWEEPER, "Minesweeper", "Find the mines", "D\xC3\xA9mineur" }, #if defined(LilyGo_TDeck_Pro) && !defined(MECK_40MHZ_TEST) - { GAME_GBC, "Game Boy", "GB / GBC emulator" }, + { GAME_GBC, "Game Boy", "GB / GBC emulator", "Game Boy" }, #endif // { GAME_2048, "2048", "Slide and merge" }, }; @@ -112,7 +114,7 @@ public: // --- Header --- display.setColor(DisplayDriver::GREEN); display.setCursor(2, 2); - display.print("Games"); + display.print(MECK_TR("Games", "Jeux")); display.setColor(DisplayDriver::LIGHT); display.drawRect(0, 12, display.width(), 1); @@ -136,7 +138,7 @@ public: display.setColor(DisplayDriver::LIGHT); } - display.drawTextEllipsized(6, y, display.width() - 12, getGames()[i].name); + display.drawTextEllipsized(6, y, display.width() - 12, MECK_TR(getGames()[i].name, getGames()[i].name_fr)); y += lineH; } @@ -147,7 +149,7 @@ public: int fy = display.height() - 12; display.drawRect(0, fy - 2, display.width(), 1); display.setCursor(2, fy); - display.print("Enter:Play Q:Back"); + display.print(MECK_TR("Enter:Play Q:Back", "Ent:Jouer Q:Retour")); return 5000; // Static menu -- slow refresh } diff --git a/examples/companion_radio/ui-new/MapScreen.h b/examples/companion_radio/ui-new/MapScreen.h index 0b1b3cb8..2643865e 100644 --- a/examples/companion_radio/ui-new/MapScreen.h +++ b/examples/companion_radio/ui-new/MapScreen.h @@ -84,6 +84,7 @@ #undef local // PNGdec's zutil.h defines 'local' as 'static' — breaks any variable named 'local' #include #include +#include "MeckLang.h" #include // --------------------------------------------------------------------------- @@ -259,9 +260,9 @@ public: display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); display.setCursor(10, 20); - display.print("SD card not found"); + display.print(MECK_TR("SD card not found", "Carte SD introuvable")); display.setCursor(10, 35); - display.print("Insert SD with"); + display.print(MECK_TR("Insert SD with", "Ins\xC3\xA9rez une SD avec")); display.setCursor(10, 48); display.print("/tiles/{z}/{x}/{y}.png"); return 5000; @@ -909,7 +910,7 @@ private: display.print(left); // Right: navigation hint - const char* right = "WASD:pan Z/X:zoom"; + const char* right = MECK_TR("WASD:pan Z/X:zoom", "WASD:bouger Z/X:zoom"); display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); } diff --git a/examples/companion_radio/ui-new/MinesweeperScreen.h b/examples/companion_radio/ui-new/MinesweeperScreen.h index cd6b6943..291b08a5 100644 --- a/examples/companion_radio/ui-new/MinesweeperScreen.h +++ b/examples/companion_radio/ui-new/MinesweeperScreen.h @@ -12,6 +12,7 @@ #include #include +#include "MeckLang.h" // Forward declarations class UITask; @@ -373,35 +374,35 @@ public: // --- READY: header + instructions + footer --- display.setColor(DisplayDriver::GREEN); display.setCursor(2, 2); - display.print("Minesweeper"); + display.print(MECK_TR("Minesweeper", "D\xC3\xA9mineur")); display.setColor(DisplayDriver::LIGHT); display.drawRect(0, MINE_HDR - 2, display.width(), 1); int cx = display.width() / 2; int y = MINE_HDR + 10; display.setColor(DisplayDriver::LIGHT); - display.drawTextCentered(cx, y, "Minesweeper"); + display.drawTextCentered(cx, y, MECK_TR("Minesweeper", "D\xC3\xA9mineur")); y += 16; display.setColor(DisplayDriver::GREEN); - display.drawTextCentered(cx, y, "W/S/A/D to move cursor"); + display.drawTextCentered(cx, y, MECK_TR("W/S/A/D to move cursor", "W/S/A/D pour se d\xC3\xA9placer")); y += 11; - display.drawTextCentered(cx, y, "Enter to reveal a cell"); + display.drawTextCentered(cx, y, MECK_TR("Enter to reveal a cell", "Entr\xC3\xA9" "e : r\xC3\xA9v\xC3\xA9ler une case")); y += 11; - display.drawTextCentered(cx, y, "F to flag a mine"); + display.drawTextCentered(cx, y, MECK_TR("F to flag a mine", "F : marquer une mine")); y += 16; display.setColor(DisplayDriver::LIGHT); char info[32]; - snprintf(info, sizeof(info), "%dx%d grid, %d mines", MINE_GRID_W, MINE_GRID_H, MINE_COUNT); + snprintf(info, sizeof(info), MECK_TR("%dx%d grid, %d mines", "Grille %dx%d, %d mines"), MINE_GRID_W, MINE_GRID_H, MINE_COUNT); display.drawTextCentered(cx, y, info); y += 16; - display.drawTextCentered(cx, y, "Press Enter to start"); + display.drawTextCentered(cx, y, MECK_TR("Press Enter to start", "Entr\xC3\xA9" "e pour commencer")); // Footer display.setColor(DisplayDriver::LIGHT); int fy = display.height() - 12; display.drawRect(0, fy - 2, display.width(), 1); display.setCursor(2, fy); - display.print("Enter:Start Sh+Del:Back"); + display.print(MECK_TR("Enter:Start Sh+Del:Back", "Ent:Jouer Sh+Del:Retour")); return 5000; } else if (_state == PLAYING) { @@ -450,13 +451,13 @@ public: int ty = boxY + 10; if (_state == WON) { display.setColor(DisplayDriver::YELLOW); - display.drawTextCentered(cx, ty, "Cleared!"); + display.drawTextCentered(cx, ty, MECK_TR("Cleared!", "Gagn\xC3\xA9 !")); } else { - display.drawTextCentered(cx, ty, "Boom!"); + display.drawTextCentered(cx, ty, MECK_TR("Boom!", "Boum !")); } ty += 16; display.setColor(DisplayDriver::GREEN); - display.drawTextCentered(cx, ty, "Enter:Retry Sh+Del:Back"); + display.drawTextCentered(cx, ty, MECK_TR("Enter:Retry Sh+Del:Back", "Ent:Rejouer Sh+Del:Retour")); return 5000; } diff --git a/examples/companion_radio/ui-new/NotesScreen.h b/examples/companion_radio/ui-new/NotesScreen.h index 28ada5a8..cd8e9d7f 100644 --- a/examples/companion_radio/ui-new/NotesScreen.h +++ b/examples/companion_radio/ui-new/NotesScreen.h @@ -2,6 +2,7 @@ #include #include +#include "MeckLang.h" #include #include #include "Utf8CP437.h" @@ -503,7 +504,7 @@ private: int rightX = display.width() - display.getTextWidth(tmp) - 2; if (_selectedFile >= 1 && _selectedFile <= (int)_fileList.size()) { - const char* hint = "[R:Rename]"; + const char* hint = MECK_TR("[R:Rename]", "[R:Renommer]"); int hintX = rightX - display.getTextWidth(hint) - 4; display.setCursor(hintX, 0); display.setColor(DisplayDriver::YELLOW); @@ -544,7 +545,7 @@ private: if (i == 0) { display.setColor(selected ? DisplayDriver::DARK : DisplayDriver::GREEN); display.drawTextEllipsized(0, y, display.width() - 4, - selected ? "> + New Note" : " + New Note"); + selected ? MECK_TR("> + New Note", "> + Nouvelle note") : MECK_TR(" + New Note", " + Nouvelle note")); } else { String line = selected ? "> " : " "; line += _fileList[i - 1]; @@ -559,8 +560,8 @@ private: display.drawRect(0, footerY - 2, display.width(), 1); display.setCursor(0, footerY); display.setColor(DisplayDriver::YELLOW); - display.print("Q:Bk"); - const char* right = "Tap/Ent:Open"; + display.print(MECK_TR("Q:Bk", "Q:Ret")); + const char* right = MECK_TR("Tap/Ent:Open", "Ent:Ouvrir"); display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); } @@ -570,14 +571,14 @@ private: display.setCursor(0, 14); display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); - display.print("(empty note)"); + display.print(MECK_TR("(empty note)", "(note vide)")); int footerY = display.height() - 12; display.drawRect(0, footerY - 2, display.width(), 1); display.setColor(DisplayDriver::YELLOW); display.setCursor(0, footerY); - display.print("Q:Bk Ent:Edit"); - const char* right = "X:Delete"; + display.print(MECK_TR("Q:Bk Ent:Edit", "Q:Ret Ent:\xC3\x89" "diter")); + const char* right = MECK_TR("X:Delete", "X:Suppr"); display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); return; @@ -662,9 +663,9 @@ private: display.setColor(DisplayDriver::YELLOW); display.setCursor(0, footerY); - display.print("Q:Bk Ent:Edit"); + display.print(MECK_TR("Q:Bk Ent:Edit", "Q:Ret Ent:\xC3\x89" "diter")); - const char* right = "X:Delete"; + const char* right = MECK_TR("X:Delete", "X:Suppr"); display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); } @@ -684,7 +685,7 @@ private: if (shortName.length() > 18) { shortName = shortName.substring(0, 15) + "..."; } - snprintf(header, sizeof(header), "Edit: %s%s", + snprintf(header, sizeof(header), MECK_TR("Edit: %s%s", "\xC3\x89" "dition : %s%s"), shortName.c_str(), _dirty ? "*" : ""); display.print(header); @@ -762,14 +763,14 @@ private: char status[20]; int curPage = (_editMaxLines > 0) ? (cursorLine / _editMaxLines) + 1 : 1; int totalPg = (_editMaxLines > 0) ? max(1, (_numEditorLines + _editMaxLines - 1) / _editMaxLines) : 1; - snprintf(status, sizeof(status), "Pg %d/%d", curPage, totalPg); + snprintf(status, sizeof(status), MECK_TR("Pg %d/%d", "p. %d/%d"), curPage, totalPg); display.print(status); const char* right; if (_bufLen == 0 || !_dirty) { - right = "Q:Back"; + right = MECK_TR("Q:Back", "Q:Retour"); } else { - right = "Sh+Del:Save"; + right = MECK_TR("Sh+Del:Save", "Sh+Del:Enreg."); } display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); @@ -779,7 +780,7 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::GREEN); display.setCursor(0, 0); - display.print("Rename Note"); + display.print(MECK_TR("Rename Note", "Renommer la note")); display.setColor(DisplayDriver::LIGHT); display.drawRect(0, 11, display.width(), 1); @@ -787,7 +788,7 @@ private: // Show original name display.setCursor(0, 20); display.setColor(DisplayDriver::LIGHT); - display.print("From: "); + display.print(MECK_TR("From: ", "De : ")); display.setTextSize(_prefs->smallTextSize()); String origDisplay = _renameOriginal; if (origDisplay.length() > 30) origDisplay = origDisplay.substring(0, 27) + "..."; @@ -797,7 +798,7 @@ private: display.setTextSize(1); display.setCursor(0, 38); display.setColor(DisplayDriver::LIGHT); - display.print("To: "); + display.print(MECK_TR("To: ", "Vers : ")); display.setTextSize(_prefs->smallTextSize()); display.setColor(DisplayDriver::GREEN); @@ -809,15 +810,15 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); display.setCursor(0, 56); - display.print("(.txt added automatically)"); + display.print(MECK_TR("(.txt added automatically)", "(.txt ajout\xC3\xA9 auto.)")); // Footer int footerY = display.height() - 12; display.drawRect(0, footerY - 2, display.width(), 1); display.setColor(DisplayDriver::YELLOW); display.setCursor(0, footerY); - display.print("Q:Cancel"); - const char* right = "Ent:Confirm"; + display.print(MECK_TR("Q:Cancel", "Q:Annuler")); + const char* right = MECK_TR("Ent:Confirm", "Ent:Valider"); display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); } @@ -826,13 +827,13 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::GREEN); display.setCursor(0, 0); - display.print("Delete Note?"); + display.print(MECK_TR("Delete Note?", "Supprimer la note ?")); display.setColor(DisplayDriver::LIGHT); display.drawRect(0, 11, display.width(), 1); display.setCursor(0, 25); - display.print("File:"); + display.print(MECK_TR("File:", "Fichier :")); display.setTextSize(_prefs->smallTextSize()); display.setCursor(0, 38); @@ -843,15 +844,15 @@ private: display.setTextSize(1); display.setCursor(0, 58); display.setColor(DisplayDriver::GREEN); - display.print("This cannot be undone."); + display.print(MECK_TR("This cannot be undone.", "Action irr\xC3\xA9versible.")); // Footer int footerY = display.height() - 12; display.drawRect(0, footerY - 2, display.width(), 1); display.setColor(DisplayDriver::YELLOW); display.setCursor(0, footerY); - display.print("Q:Cancel"); - const char* right = "Ent:Delete"; + display.print(MECK_TR("Q:Cancel", "Q:Annuler")); + const char* right = MECK_TR("Ent:Delete", "Ent:Supprimer"); display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); } @@ -982,7 +983,7 @@ private: if (_renameLen > 0) { String newName = String(_renameBuf) + ".txt"; if (newName != _renameOriginal) { - drawBriefSplash("Renaming..."); + drawBriefSplash(MECK_TR("Renaming...", "Renommage...")); if (renameNote(_renameOriginal, newName)) { if (_currentFile == _renameOriginal) { _currentFile = newName; @@ -1022,7 +1023,7 @@ private: bool handleDeleteConfirmInput(char c) { // Enter - confirm delete if (c == '\r' || c == 13) { - drawBriefSplash("Deleting..."); + drawBriefSplash(MECK_TR("Deleting...", "Suppression...")); deleteNote(_deleteTarget); _deleteTarget = ""; _selectedFile = 0; @@ -1285,7 +1286,7 @@ public: void saveAndExit() { if (_dirty && _currentFile.length() > 0) { if (_bufLen > 0) { - drawBriefSplash("Saving..."); + drawBriefSplash(MECK_TR("Saving...", "Enregistrement...")); saveNote(); } else if (_dirty) { Serial.printf("Notes: Skipping empty note %s\n", _currentFile.c_str()); @@ -1304,7 +1305,7 @@ public: void deleteCurrentNote() { if (_currentFile.length() > 0) { - drawBriefSplash("Deleting..."); + drawBriefSplash(MECK_TR("Deleting...", "Suppression...")); deleteNote(_currentFile); } _dirty = false; @@ -1332,9 +1333,9 @@ public: display.setCursor(0, 20); display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); - display.print("SD card not found"); + display.print(MECK_TR("SD card not found", "Carte SD introuvable")); display.setCursor(0, 35); - display.print("Insert SD card"); + display.print(MECK_TR("Insert SD card", "Ins\xC3\xA9rez une carte SD")); return 5000; } diff --git a/examples/companion_radio/ui-new/SnakeScreen.h b/examples/companion_radio/ui-new/SnakeScreen.h index a88cd1c0..8f87c16e 100644 --- a/examples/companion_radio/ui-new/SnakeScreen.h +++ b/examples/companion_radio/ui-new/SnakeScreen.h @@ -14,6 +14,7 @@ #include #include +#include "MeckLang.h" #include // Forward declarations @@ -275,6 +276,8 @@ private: static const int dim[] = {31,28,31,30,31,30,31,31,30,31,30,31}; static const char* mn[] = {"Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"}; + static const char* mn_fr[] = {"janv.","f\xC3\xA9vr.","mars","avr.","mai","juin", + "juil.","ao\xC3\xBBt","sept.","oct.","nov.","d\xC3\xA9" "c."}; bool leap = ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); int month = 0; for (month = 0; month < 12; month++) { @@ -284,7 +287,7 @@ private: days -= d; } if (month > 11) month = 11; - snprintf(buf, bufLen, "%d %s %d", (int)(days + 1), mn[month], year); + snprintf(buf, bufLen, "%d %s %d", (int)(days + 1), MECK_TR(mn[month], mn_fr[month]), year); } public: @@ -374,7 +377,7 @@ public: display.setCursor(2, 2); display.print("Snake"); char scoreBuf[16]; - snprintf(scoreBuf, sizeof(scoreBuf), "Score: %d", _score); + snprintf(scoreBuf, sizeof(scoreBuf), MECK_TR("Score: %d", "Score : %d"), _score); int sw = display.getTextWidth(scoreBuf); display.setCursor(display.width() - sw - 2, 2); display.print(scoreBuf); @@ -385,19 +388,19 @@ public: int cx = display.width() / 2; int y = SNAKE_HDR + 6; display.setColor(DisplayDriver::LIGHT); - display.drawTextCentered(cx, y, "Classic Snake"); + display.drawTextCentered(cx, y, MECK_TR("Classic Snake", "Snake classique")); y += 14; display.setColor(DisplayDriver::GREEN); - display.drawTextCentered(cx, y, "W/S/A/D to steer"); + display.drawTextCentered(cx, y, MECK_TR("W/S/A/D to steer", "W/S/A/D pour diriger")); y += 11; - display.drawTextCentered(cx, y, "Eat food to grow"); + display.drawTextCentered(cx, y, MECK_TR("Eat food to grow", "Mangez pour grandir")); y += 11; - display.drawTextCentered(cx, y, "Steer clear of walls"); + display.drawTextCentered(cx, y, MECK_TR("Steer clear of walls", "\xC3\x89vitez les murs")); y += 16; if (_hiCount > 0) { display.setColor(DisplayDriver::LIGHT); - display.drawTextCentered(cx, y, "-- High Scores --"); + display.drawTextCentered(cx, y, MECK_TR("-- High Scores --", "-- Meilleurs scores --")); y += 12; display.setColor(DisplayDriver::GREEN); int showCount = (_hiCount < 10) ? _hiCount : 10; @@ -415,7 +418,7 @@ public: } display.setColor(DisplayDriver::LIGHT); - display.drawTextCentered(cx, y, "Press Enter to start"); + display.drawTextCentered(cx, y, MECK_TR("Press Enter to start", "Entr\xC3\xA9" "e pour commencer")); } else { if (_state == PLAYING) { unsigned long now = millis(); @@ -450,21 +453,21 @@ public: display.drawRect(boxX, boxY, boxW, boxH); int ty = boxY + 8; - display.drawTextCentered(cx, ty, "Game Over"); + display.drawTextCentered(cx, ty, MECK_TR("Game Over", "Partie termin\xC3\xA9" "e")); ty += 14; char finalScore[24]; - snprintf(finalScore, sizeof(finalScore), "Score: %d", _score); + snprintf(finalScore, sizeof(finalScore), MECK_TR("Score: %d", "Score : %d"), _score); display.drawTextCentered(cx, ty, finalScore); ty += 12; if (_newHiScore) { display.setColor(DisplayDriver::YELLOW); char rankBuf[32]; - snprintf(rankBuf, sizeof(rankBuf), "New #%d High Score!", _newHiRank + 1); + snprintf(rankBuf, sizeof(rankBuf), MECK_TR("New #%d High Score!", "Nouveau record n\xC2\xB0%d !"), _newHiRank + 1); display.drawTextCentered(cx, ty, rankBuf); ty += 12; } display.setColor(DisplayDriver::GREEN); - display.drawTextCentered(cx, ty, "Enter:Retry Sh+Del:Back"); + display.drawTextCentered(cx, ty, MECK_TR("Enter:Retry Sh+Del:Back", "Ent:Rejouer Sh+Del:Retour")); } } @@ -474,10 +477,10 @@ public: display.drawRect(0, fy - 2, display.width(), 1); if (_state == PLAYING) { display.setCursor(2, fy); - display.print("Sh+Del:Back"); + display.print(MECK_TR("Sh+Del:Back", "Sh+Del:Retour")); } else if (_state == READY) { display.setCursor(2, fy); - display.print("Enter:Start Sh+Del:Back"); + display.print(MECK_TR("Enter:Start Sh+Del:Back", "Ent:Jouer Sh+Del:Retour")); } if (_state == PLAYING) return 100; diff --git a/examples/companion_radio/ui-new/TextReaderScreen.h b/examples/companion_radio/ui-new/TextReaderScreen.h index be173c34..fab61d00 100644 --- a/examples/companion_radio/ui-new/TextReaderScreen.h +++ b/examples/companion_radio/ui-new/TextReaderScreen.h @@ -2,6 +2,7 @@ #include #include +#include "MeckLang.h" #include #include #include "Utf8CP437.h" @@ -483,9 +484,9 @@ private: _display->setTextSize(2); _display->setColor(DisplayDriver::GREEN); _display->setCursor(10, 11); - _display->print("Indexing"); + _display->print(MECK_TR("Indexing", "Indexation")); _display->setCursor(10, 21); - _display->print("Pages..."); + _display->print(MECK_TR("Pages...", "des pages...")); // Word-wrapped filename in normal text _display->setTextSize(1); @@ -526,9 +527,9 @@ private: // Virtual: y=87 and y=93 _display->setColor(DisplayDriver::LIGHT); _display->setCursor(10, 87); - _display->print("Please wait."); + _display->print(MECK_TR("Please wait.", "Patientez.")); _display->setCursor(10, 93); - _display->print("Loading shortly..."); + _display->print(MECK_TR("Loading shortly...", "Chargement imminent...")); _display->endFrame(); } @@ -544,9 +545,9 @@ private: _display->setTextSize(2); _display->setColor(DisplayDriver::GREEN); _display->setCursor(10, 11); - _display->print("Indexing"); + _display->print(MECK_TR("Indexing", "Indexation")); _display->setCursor(10, 21); - _display->print("Pages..."); + _display->print(MECK_TR("Pages...", "des pages...")); _display->setTextSize(1); _display->setColor(DisplayDriver::LIGHT); @@ -594,7 +595,7 @@ private: // "Please wait." _display->setCursor(10, 87); - _display->print("Please wait."); + _display->print(MECK_TR("Please wait.", "Patientez.")); _display->endFrame(); } @@ -850,7 +851,7 @@ private: strncpy(shortName, filename.c_str(), sizeof(shortName) - 1); shortName[sizeof(shortName) - 1] = '\0'; } - drawSplash("Converting EPUB...", "Please wait", shortName); + drawSplash(MECK_TR("Converting EPUB...", "Conversion EPUB..."), MECK_TR("Please wait", "Patientez"), shortName); Serial.printf("TextReader: Converting EPUB '%s'\n", filename.c_str()); unsigned long t0 = millis(); @@ -861,7 +862,7 @@ private: if (!ok) { Serial.println("TextReader: EPUB conversion failed!"); - drawSplash("Convert failed!", "", shortName); + drawSplash(MECK_TR("Convert failed!", "\xC3\x89" "chec de conversion !"), "", shortName); delay(2000); return; // Stay in file list } @@ -939,7 +940,7 @@ private: strncpy(shortName, actualFilename.c_str(), sizeof(shortName) - 1); shortName[sizeof(shortName) - 1] = '\0'; } - drawSplash("Indexing...", "Please wait", shortName); + drawSplash(MECK_TR("Indexing...", "Indexation..."), MECK_TR("Please wait", "Patientez"), shortName); DisplayDriver* pxd = (_prefs->large_font || _display->getFontStyle() > 0) ? _display : nullptr; if (pxd) pxd->setTextSize(_prefs->smallTextSize()); @@ -968,7 +969,7 @@ private: strncpy(shortName, actualFilename.c_str(), sizeof(shortName) - 1); shortName[sizeof(shortName) - 1] = '\0'; } - drawSplash("Indexing...", "Please wait", shortName); + drawSplash(MECK_TR("Indexing...", "Indexation..."), MECK_TR("Please wait", "Patientez"), shortName); _pagePositions.push_back(0); DisplayDriver* pxd = (_prefs->large_font || _display->getFontStyle() > 0) ? _display : nullptr; @@ -1071,7 +1072,7 @@ private: display.setTextSize(1); display.setColor(DisplayDriver::GREEN); if (isAtBooksRoot()) { - display.print("Text Reader"); + display.print(MECK_TR("Text Reader", "Lecteur de texte")); } else { // Show current subfolder name int lastSlash = _currentPath.lastIndexOf('/'); @@ -1092,11 +1093,11 @@ private: if (totalItems == 0) { display.setCursor(0, 18); display.setColor(DisplayDriver::LIGHT); - display.print("No files found"); + display.print(MECK_TR("No files found", "Aucun fichier trouv\xC3\xA9")); display.setCursor(0, 30); - display.print("Add .txt or .epub to"); + display.print(MECK_TR("Add .txt or .epub to", "Ajoutez des .txt ou .epub")); display.setCursor(0, 42); - display.print("/books/ on SD card"); + display.print(MECK_TR("/books/ on SD card", "dans /books/ sur la SD")); } else { display.setTextSize(_prefs->smallTextSize()); // Tiny font for file list int listLineH = _prefs->smallLineH(); @@ -1130,7 +1131,7 @@ private: if (type == 0) { // ".." parent directory - line += ".. (up)"; + line += MECK_TR(".. (up)", ".. (parent)"); } else if (type == 1) { // Subdirectory line += "/" + dirNameAt(i); @@ -1163,9 +1164,9 @@ private: display.setColor(DisplayDriver::YELLOW); display.setCursor(0, footerY); - display.print("Q:Bk"); + display.print(MECK_TR("Q:Bk", "Q:Ret")); - const char* right = "Tap/Ent:Open"; + const char* right = MECK_TR("Tap/Ent:Open", "Ent:Ouvrir"); display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); } @@ -1269,7 +1270,7 @@ private: if (_gotoMode) { // Go-to-page input mode — show typed digits in footer - snprintf(status, sizeof(status), "Go to: %.*s_", _gotoBufLen, _gotoBuf); + snprintf(status, sizeof(status), MECK_TR("Go to: %.*s_", "Aller \xC3\xA0 %.*s_"), _gotoBufLen, _gotoBuf); } else { sprintf(status, "%d/%d %d%%", _currentPage + 1, _totalPages, pct); } @@ -1277,7 +1278,7 @@ private: display.setCursor(0, footerY); display.print(status); - const char* right = _gotoMode ? "Ent:Go Sh+Del:Cancel" : "Entr:Pg# Q:Bk"; + const char* right = _gotoMode ? MECK_TR("Ent:Go Sh+Del:Cancel", "Ent:OK Sh+Del:Annul.") : MECK_TR("Entr:Pg# Q:Bk", "Ent:Pg# Q:Ret"); display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); } @@ -1499,7 +1500,7 @@ public: initLayout(display); // Show initial splash - drawBootSplash(0, 0, "Scanning..."); + drawBootSplash(0, 0, MECK_TR("Scanning...", "Analyse...")); Serial.println("TextReader: Boot indexing started"); // Scan for files (includes .txt and .epub) @@ -1731,9 +1732,9 @@ public: display.setCursor(0, 20); display.setTextSize(1); display.setColor(DisplayDriver::LIGHT); - display.print("SD card not found"); + display.print(MECK_TR("SD card not found", "Carte SD introuvable")); display.setCursor(0, 35); - display.print("Insert SD with /books/"); + display.print(MECK_TR("Insert SD with /books/", "Ins\xC3\xA9rez une SD avec /books/")); return 5000; } diff --git a/examples/companion_radio/ui-new/VoiceMessageScreen.h b/examples/companion_radio/ui-new/VoiceMessageScreen.h index a69d611e..25a09e5a 100644 --- a/examples/companion_radio/ui-new/VoiceMessageScreen.h +++ b/examples/companion_radio/ui-new/VoiceMessageScreen.h @@ -27,6 +27,7 @@ #include #include +#include "MeckLang.h" #include #include #include "Audio.h" @@ -997,14 +998,14 @@ private: display.setTextSize(1); display.setCursor(0, 0); - display.print("Send Voice To:"); + display.print(MECK_TR("Send Voice To:", "Envoyer le vocal \xC3\xA0 :")); display.fillRect(0, 11, display.width(), 1); if (_pickList.empty()) { display.setCursor(10, 50); - display.print("No contacts with"); + display.print(MECK_TR("No contacts with", "Aucun contact avec")); display.setCursor(10, 65); - display.print("direct path."); + display.print(MECK_TR("direct path.", "chemin direct.")); } else { int y = 14; int lineH = 12; @@ -1043,7 +1044,7 @@ private: int footerY = display.height() - 12; display.setTextSize(1); display.setCursor(0, footerY); - display.print("Ent:Send Q:Cancel"); + display.print(MECK_TR("Ent:Send Q:Cancel", "Ent:Envoyer Q:Annuler")); // No-direct-path popup. RAW_CUSTOM voice packets are direct-route only, // so a contact with no path set cannot receive one. Drawn last so it @@ -1060,13 +1061,13 @@ private: display.setColor(DisplayDriver::DARK); display.setTextSize(1); display.setCursor(bx + 6, by + 8); - display.print("No direct path"); + display.print(MECK_TR("No direct path", "Pas de chemin direct")); display.setCursor(bx + 6, by + 20); - display.print("to contact."); + display.print(MECK_TR("to contact.", "vers ce contact.")); display.setCursor(bx + 6, by + 34); - display.print("Set a path in"); + display.print(MECK_TR("Set a path in", "D\xC3\xA9" "finissez un chemin")); display.setCursor(bx + 6, by + 46); - display.print("Contacts (P key)"); + display.print(MECK_TR("Contacts (P key)", "dans Contacts (touche P)")); display.setColor(DisplayDriver::GREEN); } } @@ -1112,23 +1113,23 @@ private: // Title bar display.setCursor(0, 0); - display.print("Voice Messages"); + display.print(MECK_TR("Voice Messages", "Messages vocaux")); char countStr[16]; - snprintf(countStr, sizeof(countStr), "%d files", (int)_fileList.size()); + snprintf(countStr, sizeof(countStr), MECK_TR("%d files", "%d fichiers"), (int)_fileList.size()); // Place the count just after the title rather than right-aligned to the // screen edge. The right-aligned position pushed the trailing 's' of // "files" off the right edge, where it wrapped onto the next line. - display.setCursor(display.getTextWidth("Voice Messages") + 6, 0); + display.setCursor(display.getTextWidth(MECK_TR("Voice Messages", "Messages vocaux")) + 6, 0); display.print(countStr); display.fillRect(0, 11, display.width(), 1); // horizontal rule if (_fileList.empty()) { display.setCursor(10, 60); - display.print("No voice messages."); + display.print(MECK_TR("No voice messages.", "Aucun message vocal.")); display.setCursor(10, 80); - display.print("Hold Mic key to record."); + display.print(MECK_TR("Hold Mic key to record.", "Mic maintenu : enregistrer")); } else { // File list int y = 14; @@ -1172,11 +1173,11 @@ private: display.setTextSize(1); display.setCursor(0, footerY); if (_listPlaying) { - display.print("Playing... Q:Stop"); + display.print(MECK_TR("Playing... Q:Stop", "Lecture... Q:Arr\xC3\xAAt")); } else if (!_fileList.empty()) { - display.print("Mic:Rec Ent:Ply F:Snd D:Del"); + display.print(MECK_TR("Mic:Rec Ent:Ply F:Snd D:Del", "Mic:Enr Ent:Lire F:Env D:Sup")); } else { - display.print("Mic:Record Q:Exit"); + display.print(MECK_TR("Mic:Record Q:Exit", "Mic:Enregistrer Q:Quitter")); } // "Loading" popup while a forward-send file is read off SD and encoded. @@ -1194,9 +1195,9 @@ private: display.setColor(DisplayDriver::DARK); display.setTextSize(1); display.setCursor(bx + 6, by + 8); - display.print("Loading..."); + display.print(MECK_TR("Loading...", "Chargement...")); display.setCursor(bx + 6, by + 20); - display.print("Encoding voice"); + display.print(MECK_TR("Encoding voice", "Encodage du vocal")); display.setColor(DisplayDriver::GREEN); _loadingDrawn = true; } @@ -1207,7 +1208,7 @@ private: display.setTextSize(1); display.setCursor(0, 0); - display.print("RECORDING"); + display.print(MECK_TR("RECORDING", "ENREGISTREMENT")); // Elapsed time float elapsed = _recSamples / (float)VOICE_SAMPLE_RATE; @@ -1262,7 +1263,7 @@ private: int footerY = display.height() - 12; display.setTextSize(1); display.setCursor(0, footerY); - display.print("Release Mic to stop"); + display.print(MECK_TR("Release Mic to stop", "Rel\xC3\xA2" "chez Mic pour arr\xC3\xAAter")); } void renderReview(DisplayDriver& display) { @@ -1270,7 +1271,7 @@ private: display.setTextSize(1); display.setCursor(0, 0); - display.print("Review Recording"); + display.print(MECK_TR("Review Recording", "\xC3\x89" "couter l'enregistrement")); display.fillRect(0, 11, display.width(), 1); // horizontal rule // Filename @@ -1280,7 +1281,7 @@ private: // Duration float secs = _recSamples / (float)VOICE_SAMPLE_RATE; char durStr[32]; - snprintf(durStr, sizeof(durStr), "Duration: %.1f seconds", secs); + snprintf(durStr, sizeof(durStr), MECK_TR("Duration: %.1f seconds", "Dur\xC3\xA9" "e : %.1f secondes"), secs); display.setCursor(10, 50); display.print(durStr); @@ -1288,9 +1289,9 @@ private: uint32_t sizeBytes = 44 + _recSamples * sizeof(int16_t); char sizeStr[32]; if (sizeBytes > 1024) { - snprintf(sizeStr, sizeof(sizeStr), "Size: %.1f KB", sizeBytes / 1024.0f); + snprintf(sizeStr, sizeof(sizeStr), MECK_TR("Size: %.1f KB", "Taille : %.1f Ko"), sizeBytes / 1024.0f); } else { - snprintf(sizeStr, sizeof(sizeStr), "Size: %d bytes", sizeBytes); + snprintf(sizeStr, sizeof(sizeStr), MECK_TR("Size: %d bytes", "Taille : %d octets"), sizeBytes); } display.setCursor(10, 70); display.print(sizeStr); @@ -1299,21 +1300,21 @@ private: if (_c2Valid) { int packets = (_c2Bytes + VOICE_MESH_PAYLOAD - 1) / VOICE_MESH_PAYLOAD; char c2Str[48]; - snprintf(c2Str, sizeof(c2Str), "Codec2: %d bytes (%d pkt%s)", + snprintf(c2Str, sizeof(c2Str), MECK_TR("Codec2: %d bytes (%d pkt%s)", "Codec2 : %d o (%d paquet%s)"), _c2Bytes, packets, packets == 1 ? "" : "s"); display.setCursor(10, 90); display.print(c2Str); } else { display.setCursor(10, 90); - display.print("Codec2: encode failed"); + display.print(MECK_TR("Codec2: encode failed", "Codec2 : \xC3\xA9" "chec d'encodage")); } // Status display.setCursor(10, 110); if (_reviewPlaying) { - display.print("Playing..."); + display.print(MECK_TR("Playing...", "Lecture...")); } else { - display.print("Ready"); + display.print(MECK_TR("Ready", "Pr\xC3\xAAt")); } // Footer @@ -1321,11 +1322,11 @@ private: display.setTextSize(1); display.setCursor(0, footerY); if (_reviewPlaying) { - display.print("Q:Stop"); + display.print(MECK_TR("Q:Stop", "Q:Arr\xC3\xAAt")); } else if (_c2Valid) { - display.print("S:Send Ent:Play Mic:Redo Q:List"); + display.print(MECK_TR("S:Send Ent:Play Mic:Redo Q:List", "S:Env Ent:Lire Mic:Enr Q:Liste")); } else { - display.print("Ent:Play Mic:Redo D:Del Q:List"); + display.print(MECK_TR("Ent:Play Mic:Redo D:Del Q:List", "Ent:Lire Mic:Enr D:Sup Q:Liste")); } }