From 97b9ae5c76a54c4cc31e188dd80072ac126b2c04 Mon Sep 17 00:00:00 2001 From: Gtes Date: Mon, 19 Oct 2026 11:49:44 +0200 Subject: [PATCH] PS5: Ukrainian translation of the frontend (AI-assisted) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shelf, the options sheet, the notifications and the RetroAchievements panel in Ukrainian when the PS5's system language is Ukrainian (id 30), with the region names. Roboto already has Cyrillic: the atlas now bakes U+0400..U+045F plus Ґ/ґ (U+0490, U+0491). --- ps5/frontend/fe_i18n.cpp | 108 ++++++++++++++++++++- ps5/frontend/fe_i18n.h | 8 +- ps5/frontend/fe_text.cpp | 5 + ps5/frontend/host/achievement_web_test.cpp | 2 +- 4 files changed, 117 insertions(+), 6 deletions(-) diff --git a/ps5/frontend/fe_i18n.cpp b/ps5/frontend/fe_i18n.cpp index 61696d3e..0ff6df0c 100644 --- a/ps5/frontend/fe_i18n.cpp +++ b/ps5/frontend/fe_i18n.cpp @@ -1017,6 +1017,106 @@ const char* const kPortugueseBrazil[kCount] = { "PS5SX2: este ícone da tela inicial não indica um jogo que o PS5SX2 possa iniciar. O menu abre no lugar.", }; +const char* const kUkrainian[kCount] = { + "Грати", + "Гортати", + "Перейти", + "Налаштування гри: немає мережі", + "Немає ігор у /data/PCSX2/games", + "Завантаження обкладинок %d / %d", + "%s ГБ", + "%s МБ", + ",", + "PS5SX2: запуск", + "PS5SX2 (тестова збірка): запуск", + "PS5SX2: завантаження обкладинок: %d", + "PS5SX2: завантаження обкладинок: %d", + "Зараз грає: %s\n%s · гарної гри!", + "нативний %s", + "програмний рендеринг", + "PS5SX2: немає гри для запуску. Помістіть файли .iso або .chd у /data/PCSX2/games/ або на USB-накопичувач, після чого знову запустіть PS5SX2.", + "PS5SX2: гра не запустилася.\n%s", + "PS5SX2: не вдалося відкрити меню; повернення до гри", + "PS5SX2: гру зупинено", + "Налаштування", + "Перемістити", + "Змінити", + "Скинути", + "Назад", + "Ця гра", + "Усі ігри", + "PS5SX2: BIOS PS2 не знайдено. Скопіюйте файл BIOS (4 МБ, наприклад SCPH-70012.bin) у %s і знову запустіть гру.", + "PS5SX2: графіку гри не запущено. Запустіть гру знову; якщо це повториться, перевстановіть PS5SX2 його інсталятором.", + "Керування", + "QR-код", + "Досягнення", + "Усі", + "Розблоковані", + "Заблоковані", + "очок", + "Оновити", + "Попередня", + "Наступна", + "Завантаження досягнень...", + "Завантаження зображень значків...", + "Немає досягнень для цього фільтра.", + "У цієї гри немає опублікованих досягнень.", + "Виберіть запущену гру, щоб переглянути її досягнення.", + "Показати запущену гру", + "Запустіть гру, щоб переглянути її досягнення під час гри. Перед грою скористайтеся вкладкою «Досягнення» на полиці ігор.", + "Перегляд досягнень недоступний.", + "Увійдіть до RetroAchievements і запустіть гру.", + "Підключення до RetroAchievements...", + "Немає даних про досягнення для цієї гри.", + "Не вдалося оновити досягнення: ", + "Виберіть «Ця гра», щоб переглянути її досягнення.", + "Завершення попереднього запиту. Натисніть Трикутник, щоб завантажити цю гру.", + "Трикутник: оновити", + "Увійдіть до RetroAchievements з полиці ігор.", + "Не вдалося визначити завантажувальний файл цього диска.", + "Не вдалося встановити захищене з'єднання.", + "Не вдалося створити перегляд досягнень.", + "Не вдалося з'єднатися. Перевірте з'єднання або увійдіть знову.", + "Набір досягнень не знайдено або сервіс недоступний.", + "Не вдалося прочитати список досягнень.", + "Дані гри або облікового запису недоступні.", + "Не вдалося отримати розблоковані досягнення. Натисніть Трикутник, щоб повторити.", + "Не вдалося прочитати основний набір досягнень.", + "Завантаження скасовано. Натисніть Трикутник, щоб повторити.", + "Не вдалося запустити перегляд досягнень.", + "Розблоковано", + "Заблоковано", + "Увійдіть в обліковий запис RetroAchievements, щоб здобувати досягнення під час гри.", + "Ім'я користувача", + "Пароль", + "Ваше ім'я користувача RetroAchievements", + "Ваш пароль", + "Увійти", + "Вхід...", + "Вийти", + "Натисніть ще раз, щоб вийти", + "Ви увійшли як %s", + "Досягнення розблоковуються під час гри. Пароль не зберігається на консолі.", + "Вибрати", + "Редагувати", + "Показати пароль", + "Приховати пароль", + "Видалити", + "Готово", + "Пробіл", + "HD-текстури", + "HD-текстури готові", + "Помилка HD-текстур", + "Завантажити", + "Скасувати", + "RetroAchievements: помилка сервера", + "Не вдалося з'єднатися з RetroAchievements. Ви досі ввійшли: досягнення з'являться з наступною грою.", + "Не вдалося надіслати розблокування до RetroAchievements перед закриттям (немає з'єднання). Його не буде у вашому обліковому записі.", + "Немає BIOS PS2: ігри не запустяться. Скопіюйте BIOS (4 МБ, напр. SCPH-70012.bin) у %s", + "PS5SX2: гру цього значка на головному екрані не знайдено (%s). Підключено накопичувач? Замість цього відкривається полиця.", + "PS5SX2: цей значок на головному екрані не вказує гру, яку PS5SX2 може запустити. Замість цього відкривається полиця.", +}; + // The region names fe_games.cpp takes from a file name's first group (kRegions there), in its order. constexpr int kRegionCount = 19; const char* const kRegionsEnglish[kRegionCount] = {"USA", "Europe", "Japan", "Korea", "Asia", "World", "Australia", @@ -1042,6 +1142,9 @@ const char* const kRegionsPortuguese[kRegionCount] = {"EUA", "Europa", "Japão", const char* const kRegionsPortugueseBrazil[kRegionCount] = {"EUA", "Europa", "Japão", "Coreia", "Ásia", "Mundo", "Austrália", "França", "Alemanha", "Itália", "Espanha", "Reino Unido", "Canadá", "Brasil", "Rússia", "China", "Taiwan", "Holanda", "Suécia"}; +const char* const kRegionsUkrainian[kRegionCount] = {"США", "Європа", "Японія", "Корея", "Азія", "Весь світ", + "Австралія", "Франція", "Німеччина", "Італія", "Іспанія", "Велика Британія", "Канада", "Бразилія", "Росія", "Китай", + "Тайвань", "Нідерланди", "Швеція"}; struct Language { @@ -1060,6 +1163,7 @@ const Language kLanguages[] = { {"nl", kDutch, kRegionsDutch}, {"pt", kPortuguese, kRegionsPortuguese}, {"pt-BR", kPortugueseBrazil, kRegionsPortugueseBrazil}, + {"uk", kUkrainian, kRegionsUkrainian}, }; const Language* s_lang = &kLanguages[0]; @@ -1068,7 +1172,7 @@ std::vector s_region_over(kRegionCount); // The PS5's language ids (as the PS4's: 0 Japanese, 1 English (US), 2 French, 3 Spanish, 4 German, 5 Italian, // 6 Dutch, 7 Portuguese, 17 Portuguese (Brazil), 18 English (UK), 20 Spanish (Latin America), 22 French -// (Canada); the others have no table here). +// (Canada), 30 Ukrainian; the others have no table here). const Language* LanguageFor(int ps5) { switch (ps5) @@ -1090,6 +1194,8 @@ const Language* LanguageFor(int ps5) return &kLanguages[7]; case 17: return &kLanguages[8]; + case 30: + return &kLanguages[9]; default: return &kLanguages[0]; } diff --git a/ps5/frontend/fe_i18n.h b/ps5/frontend/fe_i18n.h index e15f170e..e1ed2520 100644 --- a/ps5/frontend/fe_i18n.h +++ b/ps5/frontend/fe_i18n.h @@ -1,9 +1,9 @@ // PS5 port frontend: the shelf's and the notifications' text in the PS5's language (vk-285-110). // // The PS5's system language (sceSystemServiceParamGetInt parameter 1) picks a table: English, French, -// Spanish (Spain and Latin America), German, Italian, Dutch and Portuguese (Portugal and Brazil). Other -// languages get English: the shelf's font has the accented Latin letters (Latin-1) but no Cyrillic, Greek -// or CJK. A file /.txt, when present, replaces entries ("hint.play = Jugar"), so a wrong +// Spanish (Spain and Latin America), German, Italian, Dutch, Portuguese (Portugal and Brazil) and Ukrainian. +// Other languages get English: the shelf's font has the accented Latin letters (Latin-1) and Cyrillic but +// no Greek or CJK. A file /.txt, when present, replaces entries ("hint.play = Jugar"), so a wrong // word can be fixed without a new build. // // Copyright (C) 2026 swordpdf @@ -132,7 +132,7 @@ void SetLanguage(int ps5_language, const std::string& lang_dir); // The text in the current language (English where the table or the file has nothing). const char* Tr(Str id); -// "en", "fr", "es", "es-419", "de", "it", "nl", "pt" or "pt-BR". +// "en", "fr", "es", "es-419", "de", "it", "nl", "pt", "pt-BR" or "uk". const char* LanguageCode(); // The override file's key of an entry ("hint.play"), for messages and tests. diff --git a/ps5/frontend/fe_text.cpp b/ps5/frontend/fe_text.cpp index 40a90692..76f1c61a 100644 --- a/ps5/frontend/fe_text.cpp +++ b/ps5/frontend/fe_text.cpp @@ -92,6 +92,11 @@ bool Fonts::Init(const uint8_t* text_font, size_t text_size, const uint8_t* icon wanted.push_back({0, cp}); for (uint32_t cp = 160; cp < 256; cp++) wanted.push_back({0, cp}); + // Cyrillic (U+0400..U+045F) plus Ґ/ґ for the Ukrainian text (fe_i18n.cpp). + for (uint32_t cp = 0x400; cp < 0x460; cp++) + wanted.push_back({0, cp}); + wanted.push_back({0, 0x490}); + wanted.push_back({0, 0x491}); // vk-285-114: 0x2039 and 0x203A, the options sheet's arrows either side of a value. const uint32_t extra[] = {0x2022, 0x2026, 0x2013, 0x2014, 0x2018, 0x2019, 0x201C, 0x201D, 0x2122, 0x2190, 0x2192, 0x25B8, 0x2039, 0x203A, 0xFFFD}; diff --git a/ps5/frontend/host/achievement_web_test.cpp b/ps5/frontend/host/achievement_web_test.cpp index 031fb3ab..7134b566 100644 --- a/ps5/frontend/host/achievement_web_test.cpp +++ b/ps5/frontend/host/achievement_web_test.cpp @@ -40,7 +40,7 @@ int main(int argc, char** argv) std::vector english; for (int i = static_cast(Str::Achievements); i < static_cast(Str::Count); ++i) english.emplace_back(Tr(static_cast(i))); - for (int language : {1, 2, 3, 20, 4, 5, 6, 7, 17, 0}) + for (int language : {1, 2, 3, 20, 4, 5, 6, 7, 17, 30, 0}) { SetLanguage(language, ""); std::unordered_set keys;