diff --git a/Source/translation_dummy.cpp b/Source/translation_dummy.cpp index e58af8d26..84e488cf0 100644 --- a/Source/translation_dummy.cpp +++ b/Source/translation_dummy.cpp @@ -1041,5 +1041,22 @@ const char *SPELL_RUNE_OF_LIGHT_NAME = P_("spell", "Rune of Light"); const char *SPELL_RUNE_OF_NOVA_NAME = P_("spell", "Rune of Nova"); const char *SPELL_RUNE_OF_IMMOLATION_NAME = P_("spell", "Rune of Immolation"); const char *SPELL_RUNE_OF_STONE_NAME = P_("spell", "Rune of Stone"); +const char *SUBTITLE_DIABEND_0 = N_("The Soulstone burns with hellfire as an eerie"); +const char *SUBTITLE_DIABEND_1 = N_("red glow blurs your vision."); +const char *SUBTITLE_DIABEND_2 = N_("Fresh blood flows into your eyes and you"); +const char *SUBTITLE_DIABEND_3 = N_("begin to hear the tormented whispers of the damned."); +const char *SUBTITLE_DIABEND_4 = N_("You have done what you knew must be done."); +const char *SUBTITLE_DIABEND_5 = N_("The essence of Diablo is contained."); +const char *SUBTITLE_DIABEND_6 = N_("For now."); +const char *SUBTITLE_DIABEND_7 = N_("You pray that you have become strong enough"); +const char *SUBTITLE_DIABEND_8 = N_("to contain the demon and keep him at bay."); +const char *SUBTITLE_DIABEND_9 = N_("Although you have been fortified by your quest,"); +const char *SUBTITLE_DIABEND_10 = N_("you can still feel him, clawing his way"); +const char *SUBTITLE_DIABEND_11 = N_("up from the dark recesses of your soul."); +const char *SUBTITLE_DIABEND_12 = N_("Fighting to retain control, your thoughts turn toward"); +const char *SUBTITLE_DIABEND_13 = N_("the ancient, mystic lands of the Far East."); +const char *SUBTITLE_DIABEND_14 = N_("Perhaps there, beyond the desolate wastes of Aranak,"); +const char *SUBTITLE_DIABEND_15 = N_("you will find an answer."); +const char *SUBTITLE_DIABEND_16 = N_("Or perhaps, salvation."); } // namespace diff --git a/Source/utils/srt_parser.cpp b/Source/utils/srt_parser.cpp index 50df4dd23..148cde835 100644 --- a/Source/utils/srt_parser.cpp +++ b/Source/utils/srt_parser.cpp @@ -7,6 +7,7 @@ #include #include "engine/assets.hpp" +#include "utils/language.h" namespace devilution { @@ -85,7 +86,9 @@ std::string GetSubtitleAtTime(const std::vector &subtitles, uint6 { for (const auto &entry : subtitles) { if (videoTimeMs >= entry.startTimeMs && videoTimeMs < entry.endTimeMs) { - return entry.text; + // Translate the subtitle text + std::string_view translated = LanguageTranslate(entry.text); + return std::string(translated); } } return ""; diff --git a/tools/extract_translation_data.py b/tools/extract_translation_data.py index 1a3f551d0..614ed8ac8 100755 --- a/tools/extract_translation_data.py +++ b/tools/extract_translation_data.py @@ -49,6 +49,68 @@ replacement_table = str.maketrans( def create_identifier(value, prefix = '', suffix = ''): return prefix + value.upper().translate(replacement_table) + suffix +def escape_cpp_string(s): + """Escape a string for use in a C++ string literal.""" + return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n') + +def process_srt_file(srt_path, temp_source, prefix="SUBTITLE"): + """Parse an SRT file and extract subtitle text for translation.""" + if not srt_path.exists(): + return + try: + with open(srt_path, 'r', encoding='utf-8') as f: + content = f.read() + except Exception: + return + + lines = content.split('\n') + text = "" + subtitle_index = 0 + i = 0 + while i < len(lines): + line = lines[i] + # Remove \r if present (matching C++ parser behavior) + if line and line.endswith('\r'): + line = line[:-1] + + # Skip empty lines (end of subtitle block) + if not line: + if text: + # Remove trailing newline from text + text = text.rstrip('\n') + if text: + var_name = f'{prefix}_{subtitle_index}' + escaped_text = escape_cpp_string(text) + write_entry(temp_source, var_name, "subtitle", escaped_text, False) + subtitle_index += 1 + text = "" + i += 1 + continue + + # Check if line is a number (subtitle index) - skip it + if line.strip().isdigit(): + i += 1 + continue + + # Check if line contains --> (timestamp line) - skip it + if '-->' in line: + i += 1 + continue + + # Otherwise it's subtitle text + if text: + text += "\n" + text += line + i += 1 + + # Handle last subtitle if file doesn't end with blank line + if text: + text = text.rstrip('\n') + if text: + var_name = f'{prefix}_{subtitle_index}' + escaped_text = escape_cpp_string(text) + write_entry(temp_source, var_name, "subtitle", escaped_text, False) + def process_files(paths, temp_source): # Classes if "classdat" in paths: @@ -140,4 +202,14 @@ with open(translation_dummy_path, 'w') as temp_source: process_files(base_paths, temp_source) process_files(hf_paths, temp_source) + # Process SRT subtitle files + srt_files = [ + root.joinpath("assets/gendata/diabend.srt"), + ] + for srt_file in srt_files: + # Extract filename without extension and convert to uppercase for prefix + filename = srt_file.stem.upper() + prefix = f"SUBTITLE_{filename}" + process_srt_file(srt_file, temp_source, prefix) + temp_source.write(f'\n}} // namespace\n')