Merge pull request #22290 from hrydgard/savestate-custom-names

Custom names for save states
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-09-14 17:22:34 -06:00
commit 21645bee54
53 files changed
+211 -21

No files matched your search

+15 -7
View File
@@ -51,9 +51,9 @@ Follow the workflow in docs/translations.md. Run langtool from
No trailing `# comments` on those lines, they'd end up inside the translation. Placeholders like `%1` and `%d` have to appear verbatim in the
translation, in whatever position the target language needs them.
**If you don't know a language well enough to be confident, leave it out.** A key that's missing
from a language file falls back to the English string at runtime, which is normal and fine - much
better than a confident guess that nobody in the project can read well enough to catch.
**If you don't know a language well enough to be confident, leave it out.** Step 4 gives those
languages the English string as a placeholder, which is much better than a confident guess that
nobody in the project can read well enough to catch.
If a language deliberately keeps the English string (a term like "Vsync" that language doesn't
translate), that's different from not knowing - include it with the English text. It gets written
@@ -65,9 +65,17 @@ Follow the workflow in docs/translations.md. Run langtool from
Note this overwrites any existing value for that key, so if the key already had human
translations, check what you're about to replace first. The section has to exist already.
4. `cargo run -- validate` - always, at the end. It must print `Found 0 problems.`
4. `cargo run -- add-new-key "$1" "$2"` - **always, every language file ends up with the key.** This
writes `Key = Key` (plain English, no `# same as English` marker, so it still reads as
outstanding work) into every language you skipped, and leaves the ones you translated alone. A
missing key would fall back to the English string at runtime anyway, but then it looks exactly
like a translated one in the files, and translators can't see what's left to do.
Don't reach for `copy-missing-lines` to do this - it fills in placeholders repo-wide, and drags
several hundred lines of unrelated housekeeping (other missing keys, obsolete keys commented out)
into your diff.
5. `cargo run -- validate` - always, at the end. It must print `Found 0 problems.`
Finally, report which languages you translated and which you skipped and why, and leave the changes
uncommitted for review unless asked otherwise. If you want the languages you skipped to carry the
English string as a visible placeholder rather than just falling back to it, that's
`cargo run -- copy-missing-lines`.
uncommitted for review unless asked otherwise.
+46 -2
View File
@@ -66,6 +66,9 @@ constexpr int SCREENSHOT_FAILURE_RETRIES = 6;
static const char * const STATE_EXTENSION = "ppst";
static const char * const UNDO_STATE_EXTENSION = "undo.ppst";
static const char * const UNDO_SCREENSHOT_EXTENSION = "undo.jpg";
// Namespaced the way UNDO_STATE_EXTENSION is, so a stray "<prefix>_<slot>.txt" someone happened to
// leave in the savestate folder isn't mistaken for a slot's name.
static const char * const NAME_EXTENSION = "name.txt";
static const char * const LOAD_UNDO_NAME = "load_undo.ppst";
@@ -469,12 +472,10 @@ int g_screenshotFailures;
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return;
}
Path fn = GenerateSaveSlotPath(gamePrefix, slot, STATE_EXTENSION);
Path fnUndo = GenerateSaveSlotPath(gamePrefix, slot, UNDO_STATE_EXTENSION);
if (!fn.empty()) {
Path shot = GenerateSaveSlotPath(gamePrefix, slot, SCREENSHOT_EXTENSION);
std::string prefix(gamePrefix);
auto renameCallback = [fn, fnUndo, prefix, slot, callback](Status status, std::string_view message, std::string_view metadata) {
if (status != Status::FAILURE) {
@@ -535,10 +536,12 @@ int g_screenshotFailures;
void DeleteSlot(std::string_view gamePrefix, int slot) {
Path fn = GenerateSaveSlotPath(gamePrefix, slot, STATE_EXTENSION);
Path shot = GenerateSaveSlotPath(gamePrefix, slot, SCREENSHOT_EXTENSION);
Path fnName = GenerateSaveSlotPath(gamePrefix, slot, NAME_EXTENSION);
if (File::Exists(fn)) {
DeleteIfExists(fn);
DeleteIfExists(shot);
DeleteIfExists(fnName);
}
Rescan(gamePrefix);
}
@@ -706,6 +709,47 @@ int g_screenshotFailures;
return GetSaveFileDateAsString(fn);
}
std::string GetSlotCustomName(std::string_view gamePrefix, int slot) {
// Most slots don't have a name file, and this gets called for every slot each time the
// pause screen builds its views - so consult the listing before touching the disk.
if (!SaveStateFileExists(gamePrefix, slot, NAME_EXTENSION)) {
return std::string();
}
Path path = GenerateSaveSlotPath(gamePrefix, slot, NAME_EXTENSION);
std::string result;
File::ReadBinaryFileToString(path, &result);
return result;
}
void SetSlotCustomName(std::string_view gamePrefix, int slot, std::string_view new_name){
const Path path = GenerateSaveSlotPath(gamePrefix, slot, NAME_EXTENSION);
if (new_name.empty()) {
// Clearing the name. Leaving an empty file behind would work, but it'd stay in the
// savestate folder for good - a slot with no name shouldn't have a name file.
DeleteIfExists(path);
} else {
File::WriteStringToFile(true, new_name, path);
}
// What we just wrote (or removed) isn't reflected in the listing GetSlotCustomName reads,
// so without this the change wouldn't show up until something else happened to rescan.
Rescan(gamePrefix);
}
std::vector<Path> GetCompanionFilePaths(const Path &statePath) {
const std::string stateExtension = std::string(".") + STATE_EXTENSION;
// A path that isn't a savestate yields nothing, which WithReplacedExtension now tells us
// rather than handing back the path itself. An undo state does work, and correctly:
// "x.undo.ppst" maps onto "x.undo.jpg".
std::vector<Path> paths;
for (const char *extension : { SCREENSHOT_EXTENSION, NAME_EXTENSION }) {
Path companion;
if (statePath.WithReplacedExtension(stateExtension, std::string(".") + extension, &companion)) {
paths.push_back(companion);
}
}
return paths;
}
std::vector<Operation> Flush() {
std::lock_guard<std::mutex> guard(mutex);
std::vector<Operation> copy = g_pendingOperations;
+9
View File
@@ -74,7 +74,16 @@ namespace SaveState {
int GetOldestSlot(std::string_view gamePrefix);
std::string GetSlotDateAsString(std::string_view gamePrefix, int slot);
std::string GetSlotCustomName(std::string_view gamePrefix, int slot);
Path GenerateSaveSlotPath(std::string_view gamePrefix, int slot, const char *extension);
void SetSlotCustomName(std::string_view gamePrefix, int slot, std::string_view newName);
// The files that sit beside a savestate and mean nothing without it: its screenshot, and the
// slot's custom name if it was given one. Anything deleting a .ppst from outside DeleteSlot -
// the savestate browser does exactly that - has to take these along, or they stay in the
// savestate folder for good. Returns nothing for a path that isn't a savestate.
std::vector<Path> GetCompanionFilePaths(const Path &statePath);
std::string GetTitle(const Path &filename);
+5 -3
View File
@@ -170,9 +170,11 @@ bool GameInfo::Delete() {
const Path &ppstPath = filePath_;
INFO_LOG(Log::System, "Deleting file %s", ppstPath.c_str());
MoveFileToTrashOrDelete(ppstPath);
Path screenshotPath;
if (filePath_.WithReplacedExtension(".ppst", ".jpg", &screenshotPath) && File::Exists(screenshotPath)) {
MoveFileToTrashOrDelete(screenshotPath);
// The screenshot and the slot's custom name are no use without the state itself.
for (const Path &companion : SaveState::GetCompanionFilePaths(ppstPath)) {
if (File::Exists(companion)) {
MoveFileToTrashOrDelete(companion);
}
}
return true;
}
+29 -2
View File
@@ -135,6 +135,7 @@ protected:
grid->Add(undoButton)->OnClick.Handle(this, &ScreenshotViewScreen::OnUndoState);
}
grid->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
grid->Add(new Choice(di->T("Rename state"), ImageID("I_EDIT_TEXT")))->OnClick.Handle(this, &ScreenshotViewScreen::OnChangeState);
scroll->Add(content);
parent->Add(scroll);
@@ -145,11 +146,13 @@ private:
void OnLoadState(UI::EventParams &e);
void OnUndoState(UI::EventParams &e);
void OnDeleteState(UI::EventParams &e);
void OnChangeState(UI::EventParams &e);
Path screenshotFilename_;
Path gamePath_;
std::string saveStatePrefix_;
std::string title_;
std::string customNameTemp_;
int slot_;
};
@@ -204,6 +207,19 @@ void ScreenshotViewScreen::OnDeleteState(UI::EventParams &e) {
}));
}
void ScreenshotViewScreen::OnChangeState(UI::EventParams &e) {
auto di = GetI18NCategory(I18NCat::DIALOG);
customNameTemp_ = SaveState::GetSlotCustomName(saveStatePrefix_, slot_);
UI::TextEditPopupScreen *popupScreen = new UI::TextEditPopupScreen(&customNameTemp_, "", di->T("Save state name"), 64);
popupScreen->OnChange.Add([this](UI::EventParams &e) {
SaveState::SetSlotCustomName(saveStatePrefix_, slot_, customNameTemp_);
TriggerFinish(DR_YES); // DR_YES signals that we need a refresh, but not to close the pause menu.
});
screenManager()->push(popupScreen);
}
class SaveSlotView : public UI::LinearLayout {
public:
SaveSlotView(std::string_view saveStatePrefix, int slot, UI::LayoutParams *layoutParams = nullptr);
@@ -226,6 +242,10 @@ public:
return SaveState::GetSlotDateAsString(saveStatePrefix_, slot_);
}
std::string GetCustomName() const {
return SaveState::GetSlotCustomName(saveStatePrefix_, slot_);
}
UI::Event OnStateLoaded;
UI::Event OnStateSaved;
UI::Event OnScreenshotClicked;
@@ -277,7 +297,6 @@ SaveSlotView::SaveSlotView(std::string_view saveStatePrefix, int slot, UI::Layou
saveStateButton_ = buttons->Add(new Button(pa->T("Save State"), new LinearLayoutParams(0.0, Gravity::G_VCENTER)));
saveStateButton_->OnClick.Handle(this, &SaveSlotView::OnSaveState);
fv->OnClick.Add([this](UI::EventParams &e) {
e.v = this;
OnScreenshotClicked.Trigger(e);
@@ -289,6 +308,14 @@ SaveSlotView::SaveSlotView(std::string_view saveStatePrefix, int slot, UI::Layou
loadStateButton_->OnClick.Handle(this, &SaveSlotView::OnLoadState);
}
std::string nameStr = SaveState::GetSlotCustomName(saveStatePrefix_, slot_);
if (!nameStr.empty()) {
TextView *nameView = new TextView(nameStr, new LinearLayoutParams(0.0, Gravity::G_VCENTER));
nameView->SetSmall(true);
lines->Add(nameView)->SetShadow(true);
}
std::string dateStr = SaveState::GetSlotDateAsString(saveStatePrefix_, slot_);
if (slot_ == g_Config.iAutoLoadSaveState - 3) {
@@ -425,7 +452,7 @@ void GamePauseScreen::CreateSavestateControls(UI::LinearLayout *leftColumnItems,
g_Config.iCurrentStateSlot = v->GetSlot();
if (SaveState::HasSaveInSlot(saveStatePrefix_, slot)) {
Path fn = v->GetScreenshotFilename();
std::string title = v->GetScreenshotTitle();
std::string title = v->GetCustomName();
Screen *screen = new ScreenshotViewScreen(fn, saveStatePrefix_, title, v->GetSlot(), gamePath_);
screenManager()->push(screen);
}
+2
View File
@@ -499,6 +499,7 @@ Old savedata detected = ‎بيانات حفظ قديمة قد إكتشفت
Options = ‎إعدادات
Password = كلمة السر
Remove = إزالة
Rename state = إعادة تسمية الحالة # AI translated
Reset = ‎إستعادة
Resize = ‎تغيير الحجم
Restart = إعادة التشغيل
@@ -507,6 +508,7 @@ Retry = ‎حاول مجدداً
Right side = الجانب الأيمن # AI translated
Save = ‎حفظ
Save completed = ‎الحفظ إكتمل.
Save state name = اسم الحالة # AI translated
Saving = ‎يحفظ\nمن فضلك إنتظر...
SavingFailed = ‎غير قادر علي حفظ البيانات.
Search = البحث
+2
View File
@@ -490,6 +490,7 @@ Old savedata detected = Köhnə qorunuş tapıldı
Options = Seçimlər
Password = Keçid sözü
Remove = Qaldır
Rename state = Durumu yenidən adlandır # AI translated
Reset = Sıfırla
Resize = Ölçülə
Restart = Yenidən başlat
@@ -498,6 +499,7 @@ Retry = Bir daha yoxla
Right side = Sağ yön
Save = Qoru
Save completed = Qorunuş tamamlandı
Save state name = Durum adı # AI translated
Saving = Qorunur\nBuyurun, Gözləyin...
SavingFailed = Veriləni qorumaq olmur.
Search = Axtar
+2
View File
@@ -489,6 +489,7 @@ Old savedata detected = Old savedata detected
Options = Опцыі
Password = Пароль
Remove = Выдаліць
Rename state = Перайменаваць стан # AI translated
Reset = Скінуць
Resize = Змяніць памер
Restart = Перазапусціць
@@ -497,6 +498,7 @@ Retry = Паўтарыць спробу
Right side = Правы бок # AI translated
Save = Захаваць
Save completed = Save completed.
Save state name = Назва стану # AI translated
Saving = Saving\nPlease wait...
SavingFailed = Unable to save data.
Search = Пошук
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Old savedata detected
Options = Опции
Password = Парола
Remove = Премахване
Rename state = Преименувай състоянието # AI translated
Reset = Нулиране
Resize = Преоразмеряване
Restart = Рестартиране
@@ -499,6 +500,7 @@ Retry = Нов опит
Right side = Дясна страна # AI translated
Save = Запази
Save completed = Съхраняването завършено.
Save state name = Име на състоянието # AI translated
Saving = Съхраняване\nМоля изчакайте...
SavingFailed = Unable to save data.
Search = Търсене
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = S'han detectar dades del joc antigues.
Options = Opcions
Password = Password
Remove = Borrar
Rename state = Reanomena l'estat # AI translated
Reset = Reiniciar
Resize = Canviar la mida
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Reintentar
Right side = Costat dret # AI translated
Save = Guardar
Save completed = Guardat completat
Save state name = Nom de l'estat # AI translated
Saving = Guardant\nEspera un moment...
SavingFailed = Les dades no s'han pogut guardar.
Search = Cercar
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Zjištěna stará uložená data
Options = Volby
Password = Password
Remove = Remove
Rename state = Přejmenovat stav # AI translated
Reset = Resetovat
Resize = Velikost
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Zkusit znovu
Right side = Pravá strana # AI translated
Save = Uložit
Save completed = Ukládání dokončeno.
Save state name = Název stavu # AI translated
Saving = Ukládání\nČekejte prosím...
SavingFailed = Nelze uložit data.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Gamle savedata fundet
Options = Optioner
Password = Password
Remove = Remove
Rename state = Omdøb spil-status # AI translated
Reset = Nulstil
Resize = Størrelse
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Prøv igen
Right side = Højre side # AI translated
Save = Gem
Save completed = Gemt.
Save state name = Navn på spil-status # AI translated
Saving = Gemmer\nVent venligst...
SavingFailed = Ikke muligt at gemme data.
Search = Search
+2
View File
@@ -490,6 +490,7 @@ Old savedata detected = Alte Speicherdaten entdeckt
Options = Optionen
Password = Passwort
Remove = Entfernen
Rename state = Spielstand umbenennen # AI translated
Reset = Zurücksetzen
Resize = Größe ändern
Restart = Neustart
@@ -498,6 +499,7 @@ Retry = Neuversuch
Right side = Rechte Seite
Save = Speichern
Save completed = Speichern abgeschlossen
Save state name = Name des Spielstands # AI translated
Saving = Speichern\nBitte warten...
SavingFailed = Daten konnten nicht gespeichert werden
Search = Suche
+2
View File
@@ -489,6 +489,7 @@ Old savedata detected = Old savedata detected
Options = Options
Password = Password
Remove = Remove
Rename state = Rename state
Reset = Reset
Resize = Resize
Restart = Restart
@@ -497,6 +498,7 @@ Retry = Retry
Right side = Sisi kanan # AI translated
Save = Save
Save completed = Mangkami dianna.
Save state name = Save state name
Saving = Di'annai\nTajammi...
SavingFailed = Unable to save data.
Search = Search
+2
View File
@@ -515,6 +515,7 @@ Old savedata detected = Old savedata detected
Options = Options
Password = Password
Remove = Remove
Rename state = Rename state
Reset = Reset
Resize = Resize
Restart = Restart
@@ -523,6 +524,7 @@ Retry = Retry
Right side = Right side
Save = Save
Save completed = Save completed.
Save state name = Save state name
Saving = Saving\nPlease wait...
SavingFailed = Unable to save data.
Search = Search
+2
View File
@@ -492,6 +492,7 @@ Old savedata detected = Encontrado guardado previo.
Options = Opciones
Password = Contraseña
Remove = Borrar
Rename state = Renombrar estado # AI translated
Reset = Restablecer
Resize = Tamaño
Restart = Reiniciar
@@ -500,6 +501,7 @@ Retry = Reintentar
Right side = Lado derecho # AI translated
Save = Guardar
Save completed = Datos guardados.
Save state name = Nombre del estado # AI translated
Saving = Guardando\nPor favor, espera un momento...
SavingFailed = No se pueden guardar los datos.
Search = Buscar
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Se han detectado datos del juego antiguos.
Options = Opciones
Password = Contraseña
Remove = Remover
Rename state = Renombrar estado # AI translated
Reset = Reiniciar
Resize = Redimencionar Tamaño
Restart = Reiniciar
@@ -499,6 +500,7 @@ Retry = Reintentar
Right side = Lado derecho # AI translated
Save = Guardar
Save completed = Los datos del juego han sido almacenados.
Save state name = Nombre del estado # AI translated
Saving = Guardando\nPor Favor espere...
SavingFailed = Los datos del juego no se pudieron guardar.
Search = Buscar
+2
View File
@@ -490,6 +490,7 @@ Old savedata detected = Old savedata detected
Options = تنظیمات
Password = رمز عبور
Remove = حذف
Rename state = تغییر نام وضعیت # AI translated
Reset = بازیابی
Resize = اندازه گیری
Restart = Restart
@@ -498,6 +499,7 @@ Retry = تلاش مجدد
Right side = سمت راست # AI translated
Save = ‎ذخیره
Save completed = ‎.ذخیره تکمیل شد
Save state name = نام وضعیت # AI translated
Saving = ‎در حال ذخیره\n...منتظر بمانید
SavingFailed = Unable to save data.
Search = جستجو
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Vanha tallennustieto havaittu
Options = Asetukset
Password = Salasana
Remove = Poista
Rename state = Nimeä tila uudelleen # AI translated
Reset = Nollaa
Resize = Muuta kokoa
Restart = Käynnistä uudelleen
@@ -499,6 +500,7 @@ Retry = Retry
Right side = Oikea puoli # AI translated
Save = Tallenna
Save completed = Tallennus onnistui.
Save state name = Tilatallennuksen nimi # AI translated
Saving = Tallentaa\nOdota...
SavingFailed = Ei voitu tallentaa.
Search = Etsi
+2
View File
@@ -515,6 +515,7 @@ Old savedata detected = Ancienne sauvegarde détectée
Options = Options
Password = Password
Remove = Remove
Rename state = Renommer l'état # AI translated
Reset = Remise à 0
Resize = Zoomer
Restart = Restart
@@ -523,6 +524,7 @@ Retry = Réessayer
Right side = Côté droit
Save = Sauvegarder
Save completed = Sauvegarde terminée.
Save state name = Nom de l'état # AI translated
Saving = Sauvegarde\nVeuillez patienter...
SavingFailed = Sauvegarde impossible.
Search = Rechercher
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Detectados datos de gardado antigos.
Options = Options
Password = Password
Remove = Remove
Rename state = Renomear estado # AI translated
Reset = Reiniciar
Resize = Tamaño
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Reintentar
Right side = Lado dereito # AI translated
Save = Gardar
Save completed = Gardado completado.
Save state name = Nome do estado # AI translated
Saving = Gardando\nAgarda un momento...
SavingFailed = Os datos non se puideron gardar.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Εντοπίστηκαν παλαιά αρχεία απ
Options = ΕΠΙΛΟΓΕΣ
Password = Password
Remove = Remove
Rename state = Μετονομασία κατάστασης # AI translated
Reset = ΕΠΑΝΑΦΟΡΑ
Resize = ΑΛΛΑΓΗ ΜΕΓΕΘΟΥΣ
Restart = Restart
@@ -499,6 +500,7 @@ Retry = ΕΠΑΝΑΛΗΨΗ
Right side = Δεξιά πλευρά # AI translated
Save = ΑΠΟΘΗΚΕΥΣΗ
Save completed = ΑΠΟΘΗΚΕΥΣΗ ΟΛΟΚΛΗΡΩΘΗΚΕ.
Save state name = Όνομα κατάστασης # AI translated
Saving = ΑΠΟΘΗΚΕΥΣΗ\nΠΑΡΑΚΑΛΩ ΠΕΡΙΜΕΝΕΤΕ...
SavingFailed = ΑΔΥΝΑΜΙΑ ΑΠΟΘΗΚΕΥΣΗΣ ΔΕΔΟΜΕΝΩΝ.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Old savedata detected
Options = Options
Password = Password
Remove = Remove
Rename state = שנה שם למצב # AI translated
Reset = Reset
Resize = Resize
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Retry
Right side = צד ימין # AI translated
Save = Save
Save completed = .המלשוה הרימש
Save state name = שם המצב # AI translated
Saving = ...ןתמה אנא ...רמוש
SavingFailed = Unable to save data.
Search = Search
+2
View File
@@ -490,6 +490,7 @@ Old savedata detected = Old savedata detected
Options = Options
Password = Password
Remove = Remove
Rename state = בצמל םש הנש # AI translated
Reset = Reset
Resize = Resize
Restart = Restart
@@ -498,6 +499,7 @@ Retry = Retry
Right side = ןימי דצ # AI translated
Save = Save
Save completed = .המלשוה הרימש
Save state name = בצמה םש # AI translated
Saving = ...ןתמה אנא ...רמוש
SavingFailed = Unable to save data.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Uočena stara savedata
Options = Opcije
Password = Password
Remove = Remove
Rename state = Preimenuj state # AI translated
Reset = Reset
Resize = Resize
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Ponovi
Right side = Desna strana # AI translated
Save = Spremi
Save completed = Spremanje dovršeno.
Save state name = Save state name
Saving = Spremanje\nPričekajte...
SavingFailed = Nije moguće spremiti datu.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Régi mentés észlelve
Options = Opciók
Password = Password
Remove = Remove
Rename state = Állapot átnevezése # AI translated
Reset = Visszaállít
Resize = Átméretez
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Újra
Right side = Jobb oldal # AI translated
Save = Mentés
Save completed = Mentve.
Save state name = Állapot neve # AI translated
Saving = Mentés\nKérlek várj...
SavingFailed = Mentés sikertelen.
Search = Keresés
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Simpanan data lama terdeteksi
Options = Pilihan
Password = Kata sandi
Remove = Menghapus
Rename state = Ganti nama savestate # AI translated
Reset = Atur ulang
Resize = Atur ulang ukuran
Restore purchase = Pulihkan pembelian
@@ -499,6 +500,7 @@ Restart = Mengulang kembali
Right side = Sisi kanan # AI translated
Save = Simpan
Save completed = Selesai menyimpan.
Save state name = Nama savestate # AI translated
Saving = Menyimpan\nMohon tunggu...
SavingFailed = Tidak dapat menyimpan data.
Search = Cari
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Rilevati vecchi dati salvati
Options = Opzioni
Password = Password
Remove = Rimuovi
Rename state = Rinomina stato # AI translated
Reset = Reset
Resize = Ridimensiona
Restart = Riavvia
@@ -499,6 +500,7 @@ Retry = Riprova
Right side = Lato destro # AI translated
Save = Salva
Save completed = Salvataggio Completato.
Save state name = Nome dello stato # AI translated
Saving = Salvataggio in corso.\nAttendere, prego...
SavingFailed = Impossibile salvare i dati.
Search = Cerca
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = 古いセーブデータを検出しました
Options = オプション
Password = パスワード
Remove = 削除
Rename state = ステート名を変更 # AI translated
Reset = リセット
Resize = リサイズ
Restart = PPSSPPを再起動します。
@@ -499,6 +500,7 @@ Retry = リトライ
Right side = 右側 # AI translated
Save = セーブ
Save completed = セーブが完了しました。
Save state name = ステート名 # AI translated
Saving = セーブ中です。\nしばらくお待ちください...
SavingFailed = データをセーブできませんでした。
Search = 検索
+2
View File
@@ -489,6 +489,7 @@ Old savedata detected = Simpenan lawas terdeteksi
Options = Opsi
Password = Password
Remove = Remove
Rename state = Rename state
Reset = Ngreset
Resize = Ubah Ukuran
Restart = Restart
@@ -497,6 +498,7 @@ Retry = Jajal Maning
Right side = Sisih tengen # AI translated
Save = Simpen
Save completed = Nyimpen rampung.
Save state name = Save state name
Saving = Nyimpen\nMohon nteni...
SavingFailed = Ora biso Nyimpen data.
Search = Search
+2
View File
@@ -486,6 +486,7 @@ Old savedata detected =បានរកឃើញទិន្នន័យរក្
Options =ជម្រើស
Password =ពាក្យសម្ងាត់
Remove =ដកចេញ
Rename state = Rename state
Reset =កំណត់ឡើងវិញ
Resize =ប្តូរទំហំ
Restart =ចាប់ផ្ដើមឡើងវិញ
@@ -494,6 +495,7 @@ Retry =ព្យាយាមម្តងទៀត
Right side =ផ្នែកខាងស្តាំ
Save =រក្សាទុក
Save completed =ការរក្សាទុកបានបញ្ចប់។
Save state name = Save state name
Saving =កំពុងរក្សាទុក\nសូមរង់ចាំ...
SavingFailed =មិនអាចរក្សាទុកទិន្នន័យបានទេ។
Search =ស្វែងរក
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = 오래된 저장 데이터 감지됨
Options = 옵션
Password = 비밀번호
Remove = 제거
Rename state = 저장 상태 이름 변경 # AI translated
Reset = 재설정
Resize = 크기 조정
Restart = PPSSPP를 다시 시작합니다.
@@ -499,6 +500,7 @@ Retry = 재시도
Right side = 오른쪽 # AI translated
Save = 저장
Save completed = 저장이 완료되었습니다.
Save state name = 저장 상태 이름 # AI translated
Saving = 저장 중\n잠시만 기다려 주세요...
SavingFailed = 데이터를 저장할 수 없습니다.
Search = 검색
+2
View File
@@ -503,6 +503,7 @@ Old savedata detected = Old savedata detected
Options = Options
Password = وشەی نهێنی
Remove = لابردن
Rename state = Rename state
Reset = Reset
Resize = گۆڕینی قەبارە
Restart = Restart
@@ -511,6 +512,7 @@ Retry = دووبارە هەوڵدانەوە
Right side = Awarê rast # AI translated
Save = خەزن کردن
Save completed = خەزن کرا
Save state name = Save state name
Saving = خەزن دەکرێت\nتکایە چاوەڕوان بە...
SavingFailed = ناتوانرێت زانیاریەکان خەزن بکرێت
Search = گەڕان
+2
View File
@@ -489,6 +489,7 @@ Old savedata detected = ກວດພົບຂໍ້ມູນເຊບເກົ
Options = ໂຕເລືອກ
Password = Password
Remove = Remove
Rename state = Rename state
Reset = ຕັ້ງຄ່າໃໝ່
Resize = ປັບຂະໜາດ
Restart = Restart
@@ -497,6 +498,7 @@ Retry = ລອງໃໝ່ອີກຄັ້ງ
Right side = ຂະບວນດີ # AI translated
Save = ບັນທຶກ
Save completed = ບັນທຶກຂໍ້ມູນສຳເລັດ
Save state name = Save state name
Saving = ກຳລັງບັນທຶກ\nກະລຸນາຖ້າ...
SavingFailed = ບໍ່ສາມາດບັນທຶກຂໍ້ມູນ.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Aptikti seniau išsaugoti duomenys
Options = Options
Password = Password
Remove = Remove
Rename state = Pervadinti statusą # AI translated
Reset = Perkrauti
Resize = Perkeisti dydį
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Pabandyti dar kartą
Right side = Dešinė pusė # AI translated
Save = Išsaugoti
Save completed = Išsaugojimas baigtas.
Save state name = Statuso pavadinimas # AI translated
Saving = Išsaugoma\nPrašome palaukti...
SavingFailed = Neįmanoma išsaugoti duomenų.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Data simpanan lama dikesan
Options = Pilihan
Password = Kata laluan
Remove = Buang
Rename state = Namakan semula keadaan # AI translated
Reset = Mula semula
Resize = Saiz semula
Restart = Mula semula
@@ -499,6 +500,7 @@ Retry = Cuba semula
Right side = Sisi kanan
Save = Simpan
Save completed = Menyimpan selesai.
Save state name = Nama keadaan simpanan # AI translated
Saving = Menyimpan\nSila tunggu...
SavingFailed = Tidak dapat menyimpan data.
Search = Carian
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Oude savedata gedetecteerd
Options = Opties
Password = Password
Remove = Remove
Rename state = State hernoemen # AI translated
Reset = Herstellen
Resize = Schalen
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Opnieuw
Right side = Rechte zijde # AI translated
Save = Opslaan
Save completed = Het opslaan is voltooid.
Save state name = Naam van state # AI translated
Saving = Bezig met opslaan...\nEen ogenblik geduld.
SavingFailed = Kan de data niet opslaan.
Search = Search
+2
View File
@@ -515,6 +515,7 @@ Old savedata detected = Gamle lagringsdata oppdaga
Options = Val
Password = Passord
Remove = Fjern
Rename state = Gi tilstanden nytt namn # AI translated
Reset = Tilbakestill
Resize = Endre storleik
Restart = Start på nytt
@@ -523,6 +524,7 @@ Retry = Prøv igjen
Right side = Høgre side
Save = Lagre
Save completed = Lagring fullført.
Save state name = Namn på lagringstilstand # AI translated
Saving = Lagrar\nVent litt...
SavingFailed = Klarte ikkje å lagre data.
Search = Søk
+2
View File
@@ -515,6 +515,7 @@ Old savedata detected = Gamle lagringsdata oppdaget
Options = Alternativer
Password = Passord
Remove = Fjern
Rename state = Gi tilstanden nytt navn # AI translated
Reset = Tilbakestill
Resize = Endre størrelse
Restart = Start på nytt
@@ -523,6 +524,7 @@ Retry = Prøv igjen
Right side = Høyre side # AI translated
Save = Lagre
Save completed = Ferdig lagret.
Save state name = Navn på lagringstilstand # AI translated
Saving = Lagrer\nVent litt...
SavingFailed = Kunne ikke lagre data.
Search = Søk
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Wykryto stary zapis gry
Options = Opcje
Password = Hasło
Remove = Usuń
Rename state = Zmień nazwę stanu # AI translated
Reset = Resetuj
Resize = Przeskaluj
Restart = Uruchom ponownie
@@ -499,6 +500,7 @@ Retry = Spróbuj ponownie
Right side = Prawa strona # AI translated
Save = Zapisz
Save completed = Zapisywanie zakończone.
Save state name = Nazwa stanu # AI translated
Saving = Zapisywanie\nProszę czekać...
SavingFailed = Nie można zapisać danych.
Search = Szukaj
+2
View File
@@ -515,6 +515,7 @@ Old savedata detected = Dados antigos do save detectados
Options = Opções
Password = Senha
Remove = Remover
Rename state = Renomear state # AI translated
Reset = Resetar
Resize = Re-dimensionar
Restart = Reiniciar
@@ -523,6 +524,7 @@ Retry = Tentar de novo
Right side = Lado direito
Save = Salvar
Save completed = Salvamento completado.
Save state name = Nome do state # AI translated
Saving = Salvando\nPor favor espere...
SavingFailed = Incapaz de salvar os dados.
Search = Busca
+2
View File
@@ -515,6 +515,7 @@ Old savedata detected = Dados antigos de salvamento detetados
Options = Definições
Password = Palavra-passe
Remove = Remover
Rename state = Renomear estado # AI translated
Reset = Restaurar
Resize = Redimensionar
Restart = Reiniciar
@@ -523,6 +524,7 @@ Retry = Tentar de novo
Right side = Lado direito
Save = Salvar
Save completed = Salvamento completo.
Save state name = Nome do estado # AI translated
Saving = A salvar\nPor favor espere...
SavingFailed = Não foi possível salvar os dados.
Search = Pesquisar
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Date vechi salvate detectate
Options = Options
Password = Password
Remove = Remove
Rename state = Redenumește starea # AI translated
Reset = Resetare
Resize = Modificare marime
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Reîncearcă
Right side = Partea dreaptă # AI translated
Save = Salvează
Save completed = Salvare Completă
Save state name = Numele stării # AI translated
Saving = Se Salvează\nAșteptați...
SavingFailed = Nu s-au putut salva datele.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Старый формат сохранений обна
Options = Опции
Password = Пароль
Remove = Удалить
Rename state = Переименовать состояние # AI translated
Reset = Сбросить
Resize = Размер
Restart = Перезапуск
@@ -499,6 +500,7 @@ Retry = Снова
Right side = Правая сторона
Save = Сохранить
Save completed = Сохранение завершено.
Save state name = Название состояния # AI translated
Saving = Сохранение\nПожалуйста, подождите...
SavingFailed = Не удалось сохранить.
Search = Поиск
+2
View File
@@ -492,6 +492,7 @@ Old savedata detected = Gamla sparade data hittades
Options = Inställningar
Password = Lösenord
Remove = Ta bort
Rename state = Byt namn på tillstånd # AI translated
Reset = Återställ
Resize = Ändra storlek
Restart = Starta om
@@ -500,6 +501,7 @@ Retry = Försök igen
Right side = Höger sida
Save = Spara
Save completed = Sparat.
Save state name = Namn på sparat tillstånd # AI translated
Saving = Sparar\nVänta...
SavingFailed = Kunde inte spara data.
Search = Sök
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Nahanap ang lumang SAVEDATA
Options = Mga Opsyon
Password = Password
Remove = Alisin
Rename state = Palitan ang pangalan # AI translated
Reset = I-Reset
Resize = I-Resize
Restart = I-Restart
@@ -499,6 +500,7 @@ Retry = Ulitin
Right side = Сӯи рост # AI translated
Save = I-Save
Save completed = Kumpletong na-i save.
Save state name = Pangalan ng save state # AI translated
Saving = Sandali lamang...\nKasalukuyang nagse-save
SavingFailed = Hindi ma-i save ang datos.
Search = Hanapin
+2
View File
@@ -507,6 +507,7 @@ Old savedata detected = ตรวจพบข้อมูลเซฟเก่
Options = ตัวเลือก
Password = รหัสผ่าน
Remove = ลบออก
Rename state = เปลี่ยนชื่อเซฟสเตท # AI translated
Reset = รีเซ็ต
Resize = ขนาด
Restart = เริ่มต้นใหม่
@@ -515,6 +516,7 @@ Retry = ลองใหม่
Right side = ด้านขวา
Save = บันทึก
Save completed = บันทึกข้อมูลสำเร็จ
Save state name = ชื่อเซฟสเตท # AI translated
Saving = กำลังบันทึก\nโปรดรอ...
SavingFailed = ไม่สามารถบันทึกข้อมูลนี้ได้
Search = ค้นหา
+2
View File
@@ -493,6 +493,7 @@ Old savedata detected = Eski kayıt bulundu
Options = Ayarlar
Password = Şifre
Remove = Kaldır
Rename state = Durumu yeniden adlandır # AI translated
Reset = Sıfırla
Resize = Boyutlandır
Restart = Yeniden başlat
@@ -501,6 +502,7 @@ Retry = Yeniden dene
Right side = Sağ taraf # AI translated
Save = Kaydet
Save completed = Kaydetme tamamlandı
Save state name = Durum adı # AI translated
Saving = Kaydediliyor
SavingFailed = Veri kaydedilemiyor.
Search = Ara
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Виявлено старий формат збереж
Options = Опції:
Password = Пароль
Remove = Видалити
Rename state = Перейменувати стан # AI translated
Reset = Скинути
Resize = Розмір
Restart = Перезапустіть
@@ -499,6 +500,7 @@ Retry = Повторити
Right side = Правий бік # AI translated
Save = Зберегти
Save completed = Збереження завершено.
Save state name = Назва стану # AI translated
Saving = Збереження\nБудь ласка, зачекайте...
SavingFailed = Неможливо зберегти дані.
Search = Пошук
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = Phát hiện dữ liệu cũ
Options = Tùy chọn
Password = Password
Remove = Remove
Rename state = Đổi tên save state # AI translated
Reset = Mặc định
Resize = Thay đổi kích thước
Restart = Restart
@@ -499,6 +500,7 @@ Retry = Thử lại
Right side = Bên phải # AI translated
Save = Lưu
Save completed = Đã lưu xong.
Save state name = Tên save state # AI translated
Saving = Đang lưu\nXin đợi một lúc...
SavingFailed = Không thể lưu dữ liệu này.
Search = Search
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = 检测到旧版本的存档数据
Options = 选项
Password = 密码
Remove = 删除
Rename state = 重命名即时存档 # AI translated
Reset = 重置
Resize = 调整大小
Restart = 重启
@@ -499,6 +500,7 @@ Retry = 重试
Right side = 右侧 # AI translated
Save = 保存
Save completed = 保存完成。
Save state name = 即时存档名称 # AI translated
Saving = 保存中。\n请稍候…
SavingFailed = 无法保存存档。
Search = 搜索
+2
View File
@@ -491,6 +491,7 @@ Old savedata detected = 偵測到舊版存檔資料
Options = 選項
Password = 密碼
Remove = 移除
Rename state = 重新命名存檔 # AI translated
Reset = 重設
Resize = 調整大小
Restart = 重新啟動
@@ -499,6 +500,7 @@ Retry = 重試
Right side = 右側 # AI translated
Save = 儲存
Save completed = 儲存完成
Save state name = 存檔名稱 # AI translated
Saving = 正在儲存\n請稍候…
SavingFailed = 無法儲存資料
Search = 搜尋
+13 -7
View File
@@ -26,9 +26,9 @@ from `Tools/langtool`:
changes the string all the others were just translated from. Diff `en_US.ini` afterwards to check
it didn't move.
No trailing `# comments` on those lines, they'd end up inside the translation. Placeholders have to
survive verbatim. If you don't know a language well enough, leave it out - a key that's missing from
a language file falls back to the English string at runtime, which is much better than a confident
guess. If a language deliberately keeps the English string (a term like "Vsync" that isn't
survive verbatim. If you don't know a language well enough, leave it out - step 4 gives it the
English string as a placeholder, which is much better than a confident guess. If a language
deliberately keeps the English string (a term like "Vsync" that isn't
translated), do include it with the English text - it gets written with a `# same as English`
comment, which stops langtool from trying to translate it again on every later run.
3. `cargo run -- import-single <scratch-file> <Section> "<Key>"` writes them all in, including a new
@@ -36,10 +36,16 @@ from `Tools/langtool`:
were translated from. Note it overwrites existing values for that key, so take care with keys that
already have human translations, and that the section has to exist already - langtool won't create
one.
4. `cargo run -- validate` at the end, always. It checks that placeholders survived and exits
4. `cargo run -- add-new-key <Section> "<Key>"` gives every language you skipped the English string
as a placeholder - `Key = Key`, with no `# same as English` marker, so it still reads as
outstanding work - and leaves the translated ones untouched. Always do this: every language file
should end up with the key. A missing key falls back to English at runtime anyway, but in the
files it's indistinguishable from a translated one, so translators can't see what's left.
`copy-missing-lines` does the same job across every key in the repo at once, which is a different
job - it'll bury a two-string change under several hundred lines of unrelated housekeeping.
5. `cargo run -- validate` at the end, always. It checks that placeholders survived and exits
non-zero if anything is off.
Optionally follow up with `cargo run -- copy-missing-lines` to give the languages you skipped the
English string as a placeholder. The other mechanical jobs (renaming and moving keys, sorting
sections) are langtool commands too - prefer them over editing the ini files by hand.
The other mechanical jobs (renaming and moving keys, sorting sections) are langtool commands too -
prefer them over editing the ini files by hand.