Browse Source

Unify localized string handling.

pull/276/head
Adam Johnson 3 years ago
parent
commit
fb493781b3
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 16
      korman/exporter/convert.py
  2. 67
      korman/exporter/locman.py
  3. 9
      korman/properties/modifiers/base.py
  4. 28
      korman/properties/modifiers/gui.py
  5. 11
      korman/properties/modifiers/render.py

16
korman/exporter/convert.py

@ -69,6 +69,7 @@ class Exporter:
self.report.progress_add_step("Collecting Objects") self.report.progress_add_step("Collecting Objects")
self.report.progress_add_step("Verify Competence") self.report.progress_add_step("Verify Competence")
self.report.progress_add_step("Touching the Intangible") self.report.progress_add_step("Touching the Intangible")
self.report.progress_add_step("Unifying Superstrings")
self.report.progress_add_step("Harvesting Actors") self.report.progress_add_step("Harvesting Actors")
if self._op.lighting_method != "skip": if self._op.lighting_method != "skip":
etlight.LightBaker.add_progress_steps(self.report) etlight.LightBaker.add_progress_steps(self.report)
@ -98,6 +99,9 @@ class Exporter:
# In other words, generate any ephemeral Blender objects that need to be exported. # In other words, generate any ephemeral Blender objects that need to be exported.
self._pre_export_scene_objects() self._pre_export_scene_objects()
# Step 2.3: Run through all the objects and export localization.
self._export_localization()
# Step 2.5: Run through all the objects we collected in Step 2 and see if any relationships # Step 2.5: Run through all the objects we collected in Step 2 and see if any relationships
# that the artist made requires something to have a CoordinateInterface # that the artist made requires something to have a CoordinateInterface
self._harvest_actors() self._harvest_actors()
@ -248,6 +252,18 @@ class Exporter:
return ci return ci
return so.coord.object return so.coord.object
def _export_localization(self):
self.report.progress_advance()
self.report.progress_range = len(self._objects)
inc_progress = self.report.progress_increment
self.report.msg("\nExporting localization...")
for bl_obj in self._objects:
for mod in filter(lambda x: hasattr(x, "export_localization"), bl_obj.plasma_modifiers.modifiers):
mod.export_localization(self)
inc_progress()
def _export_scene_objects(self): def _export_scene_objects(self):
self.report.progress_advance() self.report.progress_advance()
self.report.progress_range = len(self._objects) self.report.progress_range = len(self._objects)

67
korman/exporter/locman.py

@ -16,6 +16,7 @@
import bpy import bpy
from PyHSPlasma import * from PyHSPlasma import *
from collections import defaultdict
from contextlib import contextmanager from contextlib import contextmanager
import itertools import itertools
from pathlib import Path from pathlib import Path
@ -45,24 +46,16 @@ class LocalizationConverter:
self._age_name = kwargs.get("age_name") self._age_name = kwargs.get("age_name")
self._path = kwargs.get("path") self._path = kwargs.get("path")
self._version = kwargs.get("version") self._version = kwargs.get("version")
self._journals = {} self._strings = defaultdict(lambda: defaultdict(dict))
self._strings = {}
def add_journal(self, name, language, text_id, indent=0):
if text_id.is_modified:
self._report.warn("Journal '{}' translation for '{}' is modified on the disk but not reloaded in Blender.",
name, language, indent=indent)
journal = self._journals.setdefault(name, {})
journal[language] = text_id.as_string()
return True
def add_string(self, set_name, element_name, language, value): def add_string(self, set_name, element_name, language, value, indent=0):
trans_set = self._strings.setdefault(set_name, {}) self._report.msg("Accepted '{}' translation for '{}'.", element_name, language, indent=indent)
trans_element = trans_set.setdefault(element_name, {}) if isinstance(value, bpy.types.Text):
trans_element[language] = value if value.is_modified:
if self._exporter is not None and self._exporter().mgr.getVer() <= pvPots: self._report.warn("'{}' translation for '{}' is modified on the disk but not reloaded in Blender.",
return False element_name, language, indent=indent)
return True value = value.as_string()
self._strings[set_name][element_name][language] = value
@contextmanager @contextmanager
def _generate_file(self, filename, **kwargs): def _generate_file(self, filename, **kwargs):
@ -96,8 +89,9 @@ class LocalizationConverter:
stream.write(contents.encode("windows-1252", "replace")) stream.write(contents.encode("windows-1252", "replace"))
return True return True
for journal_name, translations in self._journals.items(): locs = itertools.chain(self._strings["Journals"].items(), self._strings["DynaTexts"].items())
self._report.msg("Copying Journal '{}'", journal_name, indent=1) for journal_name, translations in locs:
self._report.msg("Copying localization '{}'", journal_name, indent=1)
for language_name, value in translations.items(): for language_name, value in translations.items():
if language_name not in _SP_LANGUAGES: if language_name not in _SP_LANGUAGES:
self._report.warn("Translation '{}' will not be used because it is not supported in this version of Plasma.", self._report.warn("Translation '{}' will not be used because it is not supported in this version of Plasma.",
@ -121,30 +115,20 @@ class LocalizationConverter:
self._report.port("No 'English' nor any other suitable default translation available", indent=2) self._report.port("No 'English' nor any other suitable default translation available", indent=2)
def _generate_loc_files(self): def _generate_loc_files(self):
set_LUT = { if not self._strings:
"Journals": self._journals
}
# Merge in any manual strings, but error if dupe sets are encountered.
special_sets, string_sets = frozenset(set_LUT.keys()), frozenset(self._strings.keys())
intersection = special_sets & string_sets
assert not intersection, "Duplicate localization sets: {}".format(" ".join(intersection))
set_LUT.update(self._strings)
if not any(itertools.chain.from_iterable(set_LUT.values())):
return return
method = bpy.context.scene.world.plasma_age.localization_method method = bpy.context.scene.world.plasma_age.localization_method
if method == "single_file": if method == "single_file":
self._generate_loc_file("{}.loc".format(self._age_name), set_LUT) self._generate_loc_file("{}.loc".format(self._age_name), self._strings)
elif method in {"database", "database_back_compat"}: elif method in {"database", "database_back_compat"}:
# Where the strings are set -> element -> language: str, we want language -> set -> element: str # Where the strings are set -> element -> language: str, we want language -> set -> element: str
# This is so we can mimic pfLocalizationEditor's <agename>English.loc pathing. # This is so we can mimic pfLocalizationEditor's <agename>English.loc pathing.
database = {} database = defaultdict(lambda: defaultdict(dict))
for set_name, elements in set_LUT.items(): for set_name, elements in self._strings.items():
for element_name, translations in elements.items(): for element_name, translations in elements.items():
for language_name, value in translations.items(): for language_name, value in translations.items():
database.setdefault(language_name, {}).setdefault(set_name, {})[element_name] = value database[language_name][set_name][element_name] = value
for language_name, sets in database.items(): for language_name, sets in database.items():
self._generate_loc_file("{}{}.loc".format(self._age_name, language_name), sets, language_name) self._generate_loc_file("{}{}.loc".format(self._age_name, language_name), sets, language_name)
@ -200,7 +184,7 @@ class LocalizationConverter:
loc_path = str(Path(self._path) / "dat" / "{}.loc".format(self._age_name)) loc_path = str(Path(self._path) / "dat" / "{}.loc".format(self._age_name))
log = logger.ExportVerboseLogger if age_props.verbose else logger.ExportProgressLogger log = logger.ExportVerboseLogger if age_props.verbose else logger.ExportProgressLogger
with korlib.ConsoleToggler(age_props.show_console), log(loc_path) as self._report: with korlib.ConsoleToggler(age_props.show_console), log(loc_path) as self._report:
self._report.progress_add_step("Harvesting Journals") self._report.progress_add_step("Harvesting Translations")
self._report.progress_add_step("Generating Localization") self._report.progress_add_step("Generating Localization")
self._report.progress_start("Exporting Localization Data") self._report.progress_start("Exporting Localization Data")
@ -212,20 +196,23 @@ class LocalizationConverter:
self._report.raise_errors() self._report.raise_errors()
def _run_harvest_journals(self): def _run_harvest_journals(self):
from ..properties.modifiers import TranslationMixin
objects = bpy.context.scene.objects objects = bpy.context.scene.objects
self._report.progress_advance() self._report.progress_advance()
self._report.progress_range = len(objects) self._report.progress_range = len(objects)
inc_progress = self._report.progress_increment inc_progress = self._report.progress_increment
for i in objects: for i in objects:
journal = i.plasma_modifiers.journalbookmod for mod_type in filter(None, (getattr(j, "pl_id", None) for j in TranslationMixin.__subclasses__())):
if journal.enabled: modifier = getattr(i.plasma_modifiers, mod_type)
translations = [j for j in journal.journal_translations if j.text_id is not None] if modifier.enabled:
translations = [j for j in modifier.translations if j.text_id is not None]
if not translations: if not translations:
self._report.error("Journal '{}': No content translations available. The journal will not be exported.", self._report.error("'{}': No content translations available. The localization will not be exported.",
i.name, indent=2) i.name, indent=2)
for j in translations: for j in translations:
self.add_journal(journal.key_name, j.language, j.text_id, indent=1) self.add_string(modifier.localization_set, modifier.key_name, j.language, j.text_id, indent=1)
inc_progress() inc_progress()
def _run_generate(self): def _run_generate(self):

9
korman/properties/modifiers/base.py

@ -57,6 +57,15 @@ class PlasmaModifierProperties(bpy.types.PropertyGroup):
""" """
pass pass
# Commented out to prevent conflicts with TranslationMixin overload.
"""
def export_localization(self, exporter):
'''This is an auxiliary export phase that should only convert localization data. PRP objects
are in an undefined state and therefore should not be used.
'''
pass
"""
@property @property
def face_sort(self): def face_sort(self):
"""Indicates that the geometry's faces should be sorted by the engine""" """Indicates that the geometry's faces should be sorted by the engine"""

28
korman/properties/modifiers/gui.py

@ -123,6 +123,15 @@ class PlasmaJournalTranslation(bpy.types.PropertyGroup):
class TranslationMixin: class TranslationMixin:
def export_localization(self, exporter):
translations = [i for i in self.translations if i.text_id is not None]
if not translations:
exporter.report.error("'{}': '{}' No content translations available. The localization will not be exported.",
self.id_data.name, self.bl_label, indent=1)
return
for i in translations:
exporter.locman.add_string(self.localization_set, self.key_name, i.language, i.text_id, indent=1)
def _get_translation(self): def _get_translation(self):
# Ensure there is always a default (read: English) translation available. # Ensure there is always a default (read: English) translation available.
default_idx, default = next(((idx, translation) for idx, translation in enumerate(self.translations) default_idx, default = next(((idx, translation) for idx, translation in enumerate(self.translations)
@ -153,6 +162,10 @@ class TranslationMixin:
else: else:
self.active_translation_index = idx self.active_translation_index = idx
@property
def localization_set(self):
raise RuntimeError("TranslationMixin subclass needs a localization set getter!")
@property @property
def translations(self): def translations(self):
raise RuntimeError("TranslationMixin subclass needs a translation getter!") raise RuntimeError("TranslationMixin subclass needs a translation getter!")
@ -214,15 +227,6 @@ class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
bo.name, version, indent=2) bo.name, version, indent=2)
return return
# Export the Journal translation contents
translations = [i for i in self.journal_translations if i.text_id is not None]
if not translations:
exporter.report.error("Journal '{}': No content translations available. The journal will not be exported.",
bo.name, indent=2)
return
for i in translations:
exporter.locman.add_journal(self.key_name, i.language, i.text_id, indent=2)
if self.clickable_region is None: if self.clickable_region is None:
with utils.bmesh_object("{}_Journal_ClkRgn".format(self.key_name)) as (rgn_obj, bm): with utils.bmesh_object("{}_Journal_ClkRgn".format(self.key_name)) as (rgn_obj, bm):
bmesh.ops.create_cube(bm, size=(6.0)) bmesh.ops.create_cube(bm, size=(6.0))
@ -305,12 +309,16 @@ class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
locpath = nodes.new("PlasmaAttribStringNode") locpath = nodes.new("PlasmaAttribStringNode")
locpath.link_output(journalnode, "pfm", "LocPath") locpath.link_output(journalnode, "pfm", "LocPath")
locpath.value = "{}.Journals.{}".format(age_name, self.key_name) locpath.value = "{}.{}.{}".format(age_name, self.localization_set, self.key_name)
guitype = nodes.new("PlasmaAttribStringNode") guitype = nodes.new("PlasmaAttribStringNode")
guitype.link_output(journalnode, "pfm", "GUIType") guitype.link_output(journalnode, "pfm", "GUIType")
guitype.value = self.book_type guitype.value = self.book_type
@property
def localization_set(self):
return "Journals"
@property @property
def requires_actor(self): def requires_actor(self):
# We are too late in the export to be harvested automatically, so let's be explicit # We are too late in the export to be harvested automatically, so let's be explicit

11
korman/properties/modifiers/render.py

@ -702,11 +702,6 @@ class PlasmaLocalizedTextModifier(PlasmaModifierProperties, PlasmaModifierLogicW
def pre_export(self, exporter, bo): def pre_export(self, exporter, bo):
yield self.convert_logic(bo, age_name=exporter.age_name, version=exporter.mgr.getVer()) yield self.convert_logic(bo, age_name=exporter.age_name, version=exporter.mgr.getVer())
def export(self, exporter, bo, so):
# TODO: This should probably be pulled out into its own export pass for locs
for i in filter(None, self.translations):
exporter.locman.add_string("DynaTexts", self.key_name, i.language, i.text_id, indent=2)
def logicwiz(self, bo, tree, *, age_name, version): def logicwiz(self, bo, tree, *, age_name, version):
# Rough justice. If the dynamic text map texture doesn't request alpha, then we'll want # Rough justice. If the dynamic text map texture doesn't request alpha, then we'll want
# to explicitly clear it to the material's diffuse color. This will allow artists to trivially # to explicitly clear it to the material's diffuse color. This will allow artists to trivially
@ -722,7 +717,7 @@ class PlasmaLocalizedTextModifier(PlasmaModifierProperties, PlasmaModifierLogicW
def _create_nodes(self, bo, tree, *, age_name, version, material=None, clear_color=None): def _create_nodes(self, bo, tree, *, age_name, version, material=None, clear_color=None):
pfm_node = self._create_python_file_node(tree, "xDynTextLoc.py", _LOCALIZED_TEXT_PFM) pfm_node = self._create_python_file_node(tree, "xDynTextLoc.py", _LOCALIZED_TEXT_PFM)
loc_path = self.key_name if version <= pvPots else "{}.DynaTexts.{}".format(age_name, self.key_name) loc_path = self.key_name if version <= pvPots else "{}.{}.{}".format(age_name, self.localization_set, self.key_name)
self._create_python_attribute(pfm_node, "dynTextMap", "ptAttribDynamicMap", self._create_python_attribute(pfm_node, "dynTextMap", "ptAttribDynamicMap",
target_object=bo, material=material, texture=self.texture) target_object=bo, material=material, texture=self.texture)
@ -745,6 +740,10 @@ class PlasmaLocalizedTextModifier(PlasmaModifierProperties, PlasmaModifierLogicW
self._create_python_attribute(pfm_node, "clearColorB", value=clear_color[2]) self._create_python_attribute(pfm_node, "clearColorB", value=clear_color[2])
self._create_python_attribute(pfm_node, "clearColorA", value=1.0) self._create_python_attribute(pfm_node, "clearColorA", value=1.0)
@property
def localization_set(self):
return "DynaTexts"
def sanity_check(self): def sanity_check(self):
if self.texture is None: if self.texture is None:
raise ExportError("'{}': Localized Text modifier requires a texture", self.id_data.name) raise ExportError("'{}': Localized Text modifier requires a texture", self.id_data.name)

Loading…
Cancel
Save