diff --git a/game/scripts/utility/translation.rpy b/game/scripts/utility/translation.rpy new file mode 100644 index 00000000..e2eba6f8 --- /dev/null +++ b/game/scripts/utility/translation.rpy @@ -0,0 +1,211 @@ +rpy python 3 + +init python in cli: + import os + import collections + import itertools + from renpy.translation import quote_unicode + from renpy.parser import elide_filename + + tl_file_cache = {} + + def open_tl_file(fn, mode): + + if fn in tl_file_cache: + f = tl_file_cache[fn] + + if f.mode == mode: + return f + + # elif not f.closed: + # # PY 2 only + # f.close() + + if not os.path.exists(fn): + dn = os.path.dirname(fn) + + try: + os.makedirs(dn) + except Exception: + pass + + f = open(fn, mode, encoding="utf-8") + f.write(u"\ufeff") + + else: + f = open(fn, mode, encoding="utf-8") + + tl_file_cache[fn] = f + + return f + + def scan_strings(strings, min_priority=0, max_priority=299, common_only=False): + + strings.sort(key=lambda s : s.sort_key) + + rv = [ ] + seen = set() + + for s in strings: + + if s.priority < min_priority: + continue + + if s.priority > max_priority: + continue + + if common_only and not s.common: + continue + + if s.text in seen: + continue + + seen.add(s.text) + rv.append(s) + + return rv + + def write_strings(language, strings): + + stl = renpy.game.script.translator.strings[language] + stringfiles = collections.defaultdict(list) + seen = set() + + nstrings = len(strings)-1 + + for i, s in enumerate(strings): + n = round(float(i)/(nstrings)*100) + print("\rGenerating strings for {} ... Total progress:{} % ... Stage 2/2".format(language, n), end="") + + tlfn = renpy.translation.generation.translation_filename(s) + + if tlfn is None: + continue + + if s.text in seen: + continue + + seen.add(s.text) + + if language == "None" and tlfn == "common.rpy": + tlfn = "common.rpym" + + stringfiles[tlfn].append(s) + + for tlfn, sl in list(stringfiles.items()): + tlfn = os.path.join(renpy.config.gamedir, renpy.config.tl_directory, language, tlfn) + + f = open_tl_file(tlfn, mode="w") + f.write(u"translate {} strings:\n".format(language)) + f.write(u"\n") + + for s in sl: + original = s.text + translation = stl.translate(s.text) # Keeps translated strings + + f.write(u" # {}:{}\n".format(elide_filename(s.filename), s.line)) + f.write(u" old \"{}\"\n".format(quote_unicode(original))) + f.write(u" new \"{}\"\n".format(quote_unicode(translation))) + f.write(u"\n") + + def retranslate(): + translator = renpy.game.script.translator + generation = renpy.translation.generation + scanstrings = renpy.translation.scanstrings + + parser = renpy.arguments.ArgumentParser() + parser.add_argument("--rebuild", action="store_true", help="Rebuilds the translation pointers and adds missing entries.") + parser.add_argument("--empty", action="store_true", help="When combined with rebuild parameter, it will insert an empty string into added entires. Does nothing on its own.") + parser.add_argument("--clean", action="store_true", help="Removes translation files that are no longer present within the game files.") + parser.add_argument("--dry", action="store_true", help="Simulates the removal of translation files that are no longer present within the game files.") + parser.add_argument("--include-mods", action="store_true", help="Include mod files when generating translations.") + args = parser.parse_args() + + scripts = generation.translate_list_files() + + if not args.include_mods: + mods_dir = os.path.join(renpy.config.gamedir, "mods") + scripts = [f for f in scripts if not f.startswith(mods_dir)] + + strings = [] + nscripts = len(scripts)-1 + + for i, filepath in enumerate(scripts): + n = round(float(i)/(nscripts)*100) + + for language in translator.languages: + + print("\rGenerating dialogues for {} ... Total progress:{} % ... Stage 1/2".format(language, n), end="") + + for _, trans in translator.file_translates[filepath]: + + trans_new = translator.language_translates.get((trans.identifier, language)) + filter = generation.null_filter + + if trans_new is None: + if not args.rebuild: + continue + + trans_new = trans + + if args.empty: + filter = generation.empty_filter + + if hasattr(trans, "alternate") and (trans.alternate, language) in translator.language_translates: + continue + + fp = os.path.relpath(filepath, renpy.config.gamedir) + fp = os.path.join(renpy.config.gamedir, renpy.config.tl_directory, language, fp) + + f = open_tl_file(fp, mode="w") + + f.write(u"# {}:{}\n".format(trans.filename, trans.linenumber)) + f.write(u"translate {} {}:\n".format(language, trans.identifier.replace(".", "_"))) + f.write(u"\n") + + for n in trans.block: + f.write(u" # " + n.get_code() + "\n") + + for n in trans_new.block: + f.write(u" " + n.get_code(filter) + "\n") + + f.write(u"\n") + + strings.extend(scanstrings.scan_strings(filepath)) + + strings = scan_strings(strings) + + for language in translator.languages: + + write_strings(language, strings) + + if args.clean: + translations = [f for f in renpy.list_files() if f.startswith(renpy.config.tl_directory) and f.endswith((".rpy", ".rpym"))] + + for filepath in translations: + fn = os.path.basename(filepath) + fp = os.path.relpath(filepath, os.path.join(renpy.config.tl_directory, language)) + fp = os.path.join(renpy.config.gamedir, fp) + + if fp not in scripts: + if fn == "common.rpy": + continue + + if fp in tl_file_cache: + f = tl_file_cache.pop(tl_file_cache) + f.close() + + if args.dry: + print("Removal required: {}".format(filepath)) + else: + os.unlink(filepath) + os.unlink(filepath + "c") + + for f in list(tl_file_cache.values()): + f.close() + + tl_file_cache.clear() + + return False + + renpy.arguments.register_command("retranslate", retranslate)