One language setting; localized in-memory speech per language

This commit is contained in:
2026-10-09 23:09:07 +02:00
parent 98ec2fe9c4
commit f29af40b37
40 changed files with 77414 additions and 77240 deletions

View File

@@ -30,6 +30,19 @@ TABLE_MIN = 160 # the sound list never gets smaller than thi
ANY = "Any"
def all_languages(ws):
"""Every language the editor offers: speech WADs + subtitle and menu text languages."""
return sorted(set(ws.game.languages) | set(ws.texts.menu_languages()) | set(ws.texts.subtitle_languages()))
def editor_languages(game, language=None):
"""(language, voices): the selected language (default: the game's) and the speech archive used for it
(the language itself, or INT for languages the game has no voices for)."""
lang = (language or game.language() or "INT").upper()
voices = lang if lang in game.languages else ("INT" if "INT" in game.languages else game.languages[0])
return lang, voices
class MainWindow(QMainWindow):
def __init__(self, settings=None, runner=None, game_dir=None):
super().__init__()
@@ -40,6 +53,7 @@ class MainWindow(QMainWindow):
self.player.error.connect(lambda m: self.statusBar().showMessage(m, 5000))
self.userdata = UserData()
self.ws = None
self.language = None # the editor's language (voices, subtitles, menu text)
self.loading = False
self._build()
self._apply_output_device()
@@ -50,7 +64,7 @@ class MainWindow(QMainWindow):
self.resize(1400, 900)
gd = game_dir or default_game_dir(self.settings)
if gd:
QTimer.singleShot(0, lambda: self.load_game(gd, self.settings.get("language")))
QTimer.singleShot(0, lambda: self.load_game(gd)) # always starts in the game's language
else:
self.show_no_game()
@@ -74,11 +88,16 @@ class MainWindow(QMainWindow):
tb.addWidget(title)
tb.addSeparator()
self.cmb_lang = QComboBox()
self.cmb_lang.setToolTip("Speech language archive to browse")
self.cmb_lang.setToolTip("The one language setting of the editor: the voice lines in the Sounds tab, the "
"subtitles and the menu text.\nLanguages without their own voices (e.g. JPN, RUS) "
"use the English (INT) voice lines, like the game.")
self.cmb_lang.activated.connect(lambda _: self.load_game(self.ws.game.root, self.cmb_lang.currentText())
if self.ws else None)
tb.addWidget(QLabel(" Language "))
tb.addWidget(self.cmb_lang)
self.lbl_voices = QLabel()
self.lbl_voices.setObjectName("Muted")
tb.addWidget(self.lbl_voices)
tb.addSeparator()
self.act_revert_all = tb.addAction(icons.icon("revert"), "Revert all", self.revert_all)
self.act_revert_all.setToolTip("Put every original sound back")
@@ -270,7 +289,7 @@ class MainWindow(QMainWindow):
except GameError as e:
self.show_no_game(str(e))
return
lang = language if language in game.languages else game.language()
lang, voices = editor_languages(game, language)
self.loading = True
self.statusBar().showMessage("Loading sound list…")
self.spinner.show()
@@ -279,7 +298,7 @@ class MainWindow(QMainWindow):
def work(job):
avail = available_archives(game)
return W.Workspace(game, store, [a for a in (lang, "SFX", "BANK") if a in avail], full_backup=full,
return W.Workspace(game, store, [a for a in (voices, "SFX", "BANK") if a in avail], full_backup=full,
progress=lambda n, t: job.report("scanning game audio: %d / %d" % (n, t)))
def done(ws):
@@ -287,13 +306,14 @@ class MainWindow(QMainWindow):
self.spinner.hide()
self.ws = ws
self.settings["game_dir"] = game.root
self.settings["language"] = lang
self.save_settings()
self.language = lang
self.cmb_lang.blockSignals(True)
self.cmb_lang.clear()
self.cmb_lang.addItems(game.languages)
self.cmb_lang.addItems(all_languages(ws))
self.cmb_lang.setCurrentText(lang)
self.cmb_lang.blockSignals(False)
self.lbl_voices.setText("" if voices == lang else " voices: %s (no %s voices in the game)" % (voices, lang))
self.populate()
self._text_loaded.clear()
self._tab_changed(self.tabs.currentIndex())
@@ -482,9 +502,6 @@ class MainWindow(QMainWindow):
def show_subtitle(self, key):
"""Subtitles tab with `key` selected (from the sound editor)."""
self.tabs.setCurrentWidget(self.subs)
lang = key.split(":")[1]
if self.subs.lang != lang:
self.subs.load(lang)
self.subs.search.clear()
self.subs.cb_mod.setChecked(False)
for cb in self.subs.filters.values():
@@ -692,7 +709,7 @@ class MainWindow(QMainWindow):
if (self.settings.get("game_dir") != old.get("game_dir") or self.settings.get("store_root") != old.get("store_root")
or self.settings.get("full_backup") != old.get("full_backup")):
if self.settings.get("game_dir"):
self.load_game(self.settings["game_dir"], self.settings.get("language"))
self.load_game(self.settings["game_dir"], self.language)
def _apply_output_device(self):
name = self.settings.get("output_device")

View File

@@ -161,7 +161,7 @@ class SubtitleBox(QWidget):
self.lay.removeRow(e)
self.edits, self.keys = [], []
ws = self.win.ws
lines = ws.texts.for_sound(s) if ws and s else []
lines = ws.texts.for_sound(s, self.win.language) if ws and s else []
if not lines:
self.lbl.setText("No subtitle for this sound." if s else "")
for b in (self.b_apply, self.b_revert, self.b_open):
@@ -251,12 +251,11 @@ class TextPanel(QWidget):
self._timer.setInterval(150)
self._timer.timeout.connect(self._filter)
self.search.textChanged.connect(lambda _: self._timer.start())
self.cmb_lang = QComboBox()
self.cmb_lang.setToolTip("Language of the texts")
self.cmb_lang.activated.connect(lambda _: self.load(self.cmb_lang.currentText()))
self.lbl_lang = QLabel()
self.lbl_lang.setObjectName("Muted")
self.lbl_lang.setToolTip("Change the language with the Language list at the top of the window")
top.addWidget(self.search, 1)
top.addWidget(QLabel("Language"))
top.addWidget(self.cmb_lang)
top.addWidget(self.lbl_lang)
self.filters = {}
names = ("Character", "Category") if self.kind == "subs" else ("File",)
for name in names:
@@ -349,20 +348,16 @@ class TextPanel(QWidget):
return tx.subtitle_languages() if self.kind == "subs" else tx.menu_languages()
def load(self, lang=None):
"""(Re)load the rows of a language (default: the game's language)."""
"""(Re)load the rows of a language (default: the window's language)."""
ws = self.ws()
if not ws:
return
langs = self.languages()
lang = lang if lang in langs else (ws.game.language() if ws.game.language() in langs else
(langs[0] if langs else None))
self.cmb_lang.blockSignals(True)
self.cmb_lang.clear()
self.cmb_lang.addItems(langs)
if lang:
self.cmb_lang.setCurrentText(lang)
self.cmb_lang.blockSignals(False)
lang = lang or self.win.language
if lang not in self.languages():
lang = None
self.lang = lang
self.lbl_lang.setText("Language: %s" % lang if lang else "No %s in this language" % (
"subtitles" if self.kind == "subs" else "menu text"))
if not lang:
self.model.reset([], {})
return