Redesign translation template generator

This commit is contained in:
Binrui Dong 2021-11-25 00:12:14 +08:00
parent 21ac19b549
commit ca3a92b9e5
No known key found for this signature in database
GPG Key ID: 90159C706481004E
90 changed files with 2010 additions and 1612 deletions

2
.gitignore vendored
View File

@ -96,7 +96,7 @@ cscope.*
tags
# Generated translation source
/lang/po/cataclysm-dda.pot
/lang/po/*.pot
/lang/po/en.po
# Compiled binary translations

File diff suppressed because it is too large Load Diff

2
lang/string_extractor/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__

View File

@ -0,0 +1 @@
#

View File

@ -0,0 +1,12 @@
def get_singular_name(name):
if type(name) is dict:
if "str_sp" in name:
return name["str_sp"]
elif "str" in name:
return name["str"]
else:
raise Exception("Cannot find singular name in {}".format(name))
elif type(name) is str:
return name
else:
raise Exception("Cannot find singular name in {}".format(name))

View File

@ -0,0 +1,14 @@
from dataclasses import dataclass
@dataclass
class Message:
comments: list
origin: str
format_tag: str
context: str
text: str
text_plural: str
messages = dict()

View File

@ -0,0 +1,33 @@
import json
from .parser import parsers
def parse_json_object(json, origin):
"""
Extract strings from the JSON object.
Silently ignores JSON objects without "type" key.
Raises exception if the JSON object contains unrecognized "type".
"""
if "type" in json and type(json["type"]) is str:
json_type = json["type"].lower()
if json_type in parsers:
parsers[json_type](json, origin)
else:
raise Exception("Unrecognized JSON data type \"{}\""
.format(json_type))
def parse_json_file(file_path):
"""Extract strings from the specified JSON file."""
with open(file_path, encoding="utf-8") as fp:
json_data = json.load(fp)
try:
json_objects = json_data if type(json_data) is list else [json_data]
for json_object in json_objects:
parse_json_object(json_object, file_path)
except Exception as E:
print("Error in JSON file: '{0}'".format(file_path))
print(E)
exit(1)

View File

@ -0,0 +1,221 @@
from .parsers.achievement import parse_achievement
from .parsers.activity_type import parse_activity_type
from .parsers.ammunition_type import parse_ammunition_type
from .parsers.bionic import parse_bionic
from .parsers.body_part import parse_body_part
from .parsers.clothing_mod import parse_clothing_mod
from .parsers.construction import parse_construction
from .parsers.construction_category import parse_construction_category
from .parsers.construction_group import parse_construction_group
from .parsers.dream import parse_dream
from .parsers.effect_on_condition import parse_effect_on_condition
from .parsers.effect_type import parse_effect_type
from .parsers.event_statistic import parse_event_statistic
from .parsers.faction import parse_faction
from .parsers.fault import parse_fault
from .parsers.field_type import parse_field_type
from .parsers.furniture import parse_furniture
from .parsers.gate import parse_gate
from .parsers.generic import parse_generic
from .parsers.gun import parse_gun
from .parsers.gunmod import parse_gunmod
from .parsers.harvest import parse_harvest
from .parsers.help import parse_help
from .parsers.item_action import parse_item_action
from .parsers.item_category import parse_item_category
from .parsers.json_flag import parse_json_flag
from .parsers.keybinding import parse_keybinding
from .parsers.loot_zone import parse_loot_zone
from .parsers.map_extra import parse_map_extra
from .parsers.mapgen import parse_mapgen
from .parsers.mutation import parse_mutation
from .parsers.npc import parse_npc
from .parsers.npc_class import parse_npc_class
from .parsers.overmap_terrain import parse_overmap_terrain
from .parsers.palette import parse_palette
from .parsers.profession import parse_profession
from .parsers.proficiency import parse_proficiency
from .parsers.recipe import parse_recipe
from .parsers.recipe_category import parse_recipe_category
from .parsers.recipe_group import parse_recipe_group
from .parsers.magazine import parse_magazine
from .parsers.material import parse_material
from .parsers.martial_art import parse_martial_art
from .parsers.mission_definition import parse_mission_definition
from .parsers.mod_info import parse_mod_info
from .parsers.monster import parse_monster
from .parsers.monster_attack import parse_monster_attack
from .parsers.morale_type import parse_morale_type
from .parsers.movement_mode import parse_movement_mode
from .parsers.mutation_category import parse_mutation_category
from .parsers.overmap_land_use_code import parse_overmap_land_use_code
from .parsers.practice import parse_practice
from .parsers.scenario import parse_scenario
from .parsers.skill import parse_skill
from .parsers.skill_display_type import parse_skill_display_type
from .parsers.speech import parse_speech
from .parsers.speed_description import parse_speed_description
from .parsers.species import parse_species
from .parsers.spell import parse_spell
from .parsers.start_location import parse_start_location
from .parsers.snippet import parse_snippet
from .parsers.sub_body_part import parse_sub_body_part
from .parsers.talk_topic import parse_talk_topic
from .parsers.technique import parse_technique
from .parsers.ter_furn_transform import parse_ter_furn_transform
from .parsers.terrain import parse_terrain
from .parsers.tool_quality import parse_tool_quality
from .parsers.trap import parse_trap
from .parsers.vehicle import parse_vehicle
from .parsers.vehicle_part import parse_vehicle_part
from .parsers.vehicle_part_category import parse_vehicle_part_category
from .parsers.vehicle_spawn import parse_vehicle_spawn
from .parsers.vitamin import parse_vitamin
from .parsers.weapon_category import parse_weapon_category
from .parsers.weather_type import parse_weather_type
from .parsers.widget import parse_widget
def dummy_parser(json, origin):
return
parsers = {
"achievement": parse_achievement,
"activity_type": parse_activity_type,
"ammo": parse_generic,
"ammo_effect": dummy_parser,
"ammunition_type": parse_ammunition_type,
"anatomy": dummy_parser,
"armor": parse_generic,
"ascii_art": dummy_parser,
"battery": parse_generic,
"behavior": dummy_parser,
"bionic": parse_bionic,
"bionic_item": parse_generic,
"body_part": parse_body_part,
"book": parse_generic,
"butchery_requirement": dummy_parser,
"charge_removal_blacklist": dummy_parser,
"city_building": dummy_parser,
"clothing_mod": parse_clothing_mod,
"comestible": parse_generic,
"colordef": dummy_parser,
"conduct": parse_achievement,
"construction": parse_construction,
"construction_category": parse_construction_category,
"construction_group": parse_construction_group,
"dream": parse_dream,
"disease_type": dummy_parser,
"effect_on_condition": parse_effect_on_condition,
"effect_type": parse_effect_type,
"emit": dummy_parser,
"enchantment": dummy_parser,
"engine": parse_generic,
"event_statistic": parse_event_statistic,
"event_transformation": dummy_parser,
"external_option": dummy_parser,
"faction": parse_faction,
"fault": parse_fault,
"field_type": parse_field_type,
"furniture": parse_furniture,
"gate": parse_gate,
"generic": parse_generic,
"gun": parse_gun,
"gunmod": parse_gunmod,
"harvest": parse_harvest,
"harvest_drop_type": dummy_parser,
"help": parse_help,
"hit_range": dummy_parser,
"item_action": parse_item_action,
"item_category": parse_item_category,
"item_blacklist": dummy_parser,
"item_group": dummy_parser,
"json_flag": parse_json_flag,
"keybinding": parse_keybinding,
"loot_zone": parse_loot_zone,
"magazine": parse_magazine,
"map_extra": parse_map_extra,
"mapgen": parse_mapgen,
"martial_art": parse_martial_art,
"material": parse_material,
"migration": dummy_parser,
"mission_definition": parse_mission_definition,
"mod_info": parse_mod_info,
"mod_tileset": dummy_parser,
"monster": parse_monster,
"monster_adjustment": dummy_parser,
"monster_attack": parse_monster_attack,
"monster_blacklist": dummy_parser,
"monster_faction": dummy_parser,
"monster_whitelist": dummy_parser,
"monstergroup": dummy_parser,
"mood_face": dummy_parser,
"morale_type": parse_morale_type,
"movement_mode": parse_movement_mode,
"mutation": parse_mutation,
"mutation_category": parse_mutation_category,
"mutation_type": dummy_parser,
"npc": parse_npc,
"npc_class": parse_npc_class,
"obsolete_terrain": dummy_parser,
"overlay_order": dummy_parser,
"overmap_connection": dummy_parser,
"overmap_land_use_code": parse_overmap_land_use_code,
"overmap_location": dummy_parser,
"overmap_special": dummy_parser,
"overmap_terrain": parse_overmap_terrain,
"palette": parse_palette,
"pet_armor": parse_generic,
"playlist": dummy_parser,
"practice": parse_practice,
"profession": parse_profession,
"profession_item_substitutions": dummy_parser,
"proficiency": parse_proficiency,
"recipe": parse_recipe,
"recipe_category": parse_recipe_category,
"recipe_group": parse_recipe_group,
"region_overlay": dummy_parser,
"region_settings": dummy_parser,
"relic_procgen_data": dummy_parser,
"requirement": dummy_parser,
"rotatable_symbol": dummy_parser,
"scenario": parse_scenario,
"scenario_blacklist": dummy_parser,
"scent_type": dummy_parser,
"score": dummy_parser,
"skill": parse_skill,
"skill_boost": dummy_parser,
"skill_display_type": parse_skill_display_type,
"snippet": parse_snippet,
"sound_effect": dummy_parser,
"speech": parse_speech,
"speed_description": parse_speed_description,
"species": parse_species,
"spell": parse_spell,
"start_location": parse_start_location,
"sub_body_part": parse_sub_body_part,
"talk_topic": parse_talk_topic,
"technique": parse_technique,
"ter_furn_transform": parse_ter_furn_transform,
"terrain": parse_terrain,
"trait_blacklist": dummy_parser,
"trait_group": dummy_parser,
"trap": parse_trap,
"tool": parse_generic,
"tool_armor": parse_generic,
"tool_quality": parse_tool_quality,
"toolmod": parse_generic,
"uncraft": dummy_parser,
"vehicle": parse_vehicle,
"vehicle_group": dummy_parser,
"vehicle_part": parse_vehicle_part,
"vehicle_part_category": parse_vehicle_part_category,
"vehicle_placement": dummy_parser,
"vehicle_spawn": parse_vehicle_spawn,
"vitamin": parse_vitamin,
"weapon_category": parse_weapon_category,
"weather_type": parse_weather_type,
"wheel": parse_generic,
"widget": parse_widget
}

View File

@ -0,0 +1 @@
#

View File

@ -0,0 +1,18 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_achievement(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Name of achievement")
if "description" in json:
write_text(json["description"], origin,
comment="Description of achievement \"{}\"".format(name))
if "requirements" in json:
for req in json["requirements"]:
if "description" in req:
comment = "Description of requirement " \
"of achievement \"{}\"".format(name)
write_text(req["description"], origin, comment=comment)

View File

@ -0,0 +1,7 @@
from ..write_text import write_text
def parse_activity_type(json, origin):
if "verb" in json:
write_text(json["verb"], origin,
comment="Verb in activity \"{}\"".format(json["id"]))

View File

@ -0,0 +1,10 @@
from ..write_text import write_text
def parse_ammunition_type(json, origin):
comment = json.get("//~")
if "name" in json:
write_text(json["name"], origin, comment=[
"Name of an ammunition type",
comment
])

View File

@ -0,0 +1,13 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_bionic(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Name of a bionic")
if "description" in json:
write_text(json["description"], origin, c_format=False,
comment="Description of bionic \"{}\"".format(name))

View File

@ -0,0 +1,37 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_body_part(json, origin):
# See comments in `body_part_struct::load` of bodypart.cpp about why xxx
# and xxx_multiple are not inside a single translation object.
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Name of body part")
if "name_multiple" in json:
write_text(json["name_multiple"], origin, comment="Name of body part")
write_text(json["accusative"], origin,
comment="Accusative name of body part")
if "accusative_multiple" in json:
write_text(json["accusative_multiple"], origin,
comment="Accusative name of body part")
write_text(json["encumbrance_text"], origin,
comment="Encumbrance text of body part \"{}\"".format(name))
write_text(json["heading"], origin,
comment="Heading of body part \"{}\"".format(name))
write_text(json["heading_multiple"], origin,
comment="Heading of body part \"{}\"".format(name))
if "smash_message" in json:
write_text(json["smash_message"], origin,
comment="Smash message of body part \"{}\"".format(name))
if "hp_bar_ui_text" in json:
write_text(json["hp_bar_ui_text"], origin,
comment="HP bar UI text of body part \"{}\"".format(name))

View File

@ -0,0 +1,8 @@
from ..write_text import write_text
def parse_clothing_mod(json, origin):
write_text(json["implement_prompt"], origin,
comment="Implement clothing modification prompt text")
write_text(json["destroy_prompt"], origin,
comment="Destroy clothing modification prompt text")

View File

@ -0,0 +1,7 @@
from ..write_text import write_text
def parse_construction(json, origin):
if "pre_note" in json:
write_text(json["pre_note"], origin,
comment="Prerequisite of terrain constrution")

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_construction_category(json, origin):
if "name" in json:
write_text(json["name"], origin, comment="Construction category name")

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_construction_group(json, origin):
if "name" in json:
write_text(json["name"], origin, comment="Construction group name")

View File

@ -0,0 +1,8 @@
from ..write_text import write_text
def parse_dream(json, origin):
for msg in json.get("messages", []):
write_text(msg, origin,
comment="Dream of mutation category \"{}\""
.format(json["category"]))

View File

@ -0,0 +1,20 @@
from ..write_text import write_text
def parse_effect(effects, origin):
if not type(effects) is list:
effects = [effects]
for eff in effects:
if type(eff) is dict:
if "message" in eff:
write_text(eff["message"], origin,
comment="Message in an effect")
if "u_buy_monster" in eff and "name" in eff:
write_text(eff["name"], origin,
comment="Nickname for creature '{}'".
format(eff["u_buy_monster"]))
if "u_message" in eff:
if "snippet" in eff and eff["snippet"] is True:
continue
write_text(eff["u_message"], origin,
comment="Message about the player in an effect")

View File

@ -0,0 +1,7 @@
from .effect import parse_effect
def parse_effect_on_condition(json, origin):
parse_effect(json["effect"], origin)
if "false_effect" in json:
parse_effect(json["false_effect"], origin)

View File

@ -0,0 +1,87 @@
from ..write_text import write_text
def parse_effect_type(json, origin):
effect_name = ""
if "name" in json:
names = set()
for name in json["name"]:
if type(name) is str:
names.add(("", name))
elif type(name) is dict:
names.add((name["ctxt"], name["str"]))
for (ctxt, name) in names:
write_text(name, origin, context=ctxt,
comment="Name of effect type id \"{}\""
.format(json["id"]))
if effect_name == "":
effect_name = name
else:
effect_name = effect_name + ", " + name
if effect_name == "":
effect_name = json["id"]
if "desc" in json:
descs = set()
for desc in json["desc"]:
if type(desc) is str:
descs.add(("", desc))
elif type(desc) is dict:
descs.add((desc["ctxt"], desc["str"]))
for (ctxt, desc) in descs:
write_text(desc, origin, context=ctxt,
comment="Description of effect type \"{}\""
.format(effect_name))
if "speed_name" in json:
write_text(json["speed_name"], origin,
comment="Speed name of effect type \"{}\""
.format(effect_name))
if "apply_message" in json:
write_text(json["apply_message"], origin,
comment="Apply message of effect type \"{}\""
.format(effect_name))
if "remove_message" in json:
write_text(json["remove_message"], origin,
comment="Remove message of effect type \"{}\""
.format(effect_name))
if "death_msg" in json:
write_text(json["death_msg"], origin,
comment="Death message of effect type \"{}\""
.format(effect_name))
if "miss_messages" in json:
for msg in json["miss_messages"]:
write_text(msg[0], origin,
comment="Miss message of effect type \"{}\""
.format(effect_name))
if "decay_messages" in json:
for msg in json["decay_messages"]:
write_text(msg[0], origin,
comment="Decay message of effect type \"{}\""
.format(effect_name))
if "apply_memorial_log" in json:
write_text(json["apply_memorial_log"], origin,
context="memorial_male",
comment="Male memorial apply log of effect type \"{}\""
.format(effect_name))
write_text(json["apply_memorial_log"], origin,
context="memorial_female",
comment="Female memorial apply log of effect type \"{}\""
.format(effect_name))
if "remove_memorial_log" in json:
write_text(json["remove_memorial_log"], origin,
context="memorial_male",
comment="Male memorial remove log of effect type \"{}\""
.format(effect_name))
write_text(json["remove_memorial_log"], origin,
context="memorial_female",
comment="Female memorial remove log of effect type \"{}\""
.format(effect_name))

View File

@ -0,0 +1,9 @@
from ..write_text import write_text
def parse_event_statistic(json, origin):
if "description" in json:
write_text(json["description"], origin,
comment="Description of event statistic \"{}\""
.format(json["id"]),
plural=True)

View File

@ -0,0 +1,13 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_faction(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="NPC faction name")
if "description" in json:
write_text(json["description"], origin,
comment="Description of NPC faction \"{}\"".format(name))

View File

@ -0,0 +1,24 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_fault(json, origin):
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Name of a fault")
write_text(json["description"], origin,
comment="Description of fault \"{}\"".format(name))
for method in json["mending_methods"]:
method_name = get_singular_name(method["name"])
write_text(method["name"], origin,
comment="Name of mending method to fault \"{}\""
.format(name))
if "description" in method:
write_text(method["description"], origin,
comment="Description of mending method "
"\"{0}\" to fault \"{1}\""
.format(name, method_name))
if "success_msg" in method:
write_text(method["success_msg"], origin,
comment="Success message of mending method "
"\"{0}\" to fault \"{1}\""
.format(name, method_name))

View File

@ -0,0 +1,7 @@
from ..write_text import write_text
def parse_field_type(json, origin):
for fd in json.get("intensity_levels", []):
if "name" in fd:
write_text(fd["name"], origin, comment="Field intensity level")

View File

@ -0,0 +1,20 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_furniture(json, origin):
name = get_singular_name(json.get("name", json["id"]))
if "name" in json:
write_text(json["name"], origin, comment="Furniture name")
if "description" in json:
write_text(json["description"], origin,
comment="Description of furniture \"{}\"".format(name))
if "bash" in json:
if "sound" in json["bash"]:
write_text(json["bash"]["sound"], origin,
comment="Bashing sound of furniture \"{}\"".
format(name))
if "sound_fail" in json["bash"]:
write_text(json["bash"]["sound_fail"], origin,
comment="Bashing failed sound of furniture \"{}\""
.format(name))

View File

@ -0,0 +1,9 @@
from ..write_text import write_text
def parse_gate(json, origin):
messages = json.get("messages", {})
for i in messages:
write_text(messages[i], origin,
comment="Message of {} action on a gate".format(i))

View File

@ -0,0 +1,60 @@
from ..helper import get_singular_name
from .use_action import parse_use_action
from ..write_text import write_text
def parse_generic(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Item name",
plural=True, c_format=False)
elif "id" in json:
name = json["id"]
if "description" in json:
write_text(json["description"], origin, c_format=False,
comment="Description of \"{}\"".format(name))
if "use_action" in json:
parse_use_action(json["use_action"], origin, name)
for cname in json.get("conditional_names", []):
write_text(cname["name"], origin,
comment="Conditional name for \"{}\" when {} matches {}"
.format(name, cname["type"], cname["condition"]),
plural=True)
if "snippet_category" in json and type(json["snippet_category"]) is list:
# snippet_category is either a simple string (the category ident)
# which is not translated, or an array of snippet texts.
for entry in json["snippet_category"]:
if type(entry) is str:
write_text(entry, origin,
comment="Snippet of item \"{}\"".format(name))
elif type(entry) is dict:
write_text(entry["text"], origin,
comment="Snippet of item \"{}\"".format(name))
if "seed_data" in json:
write_text(json["seed_data"]["plant_name"], origin,
comment="Plant name of seed \"{}\"".format(name))
if "revert_msg" in json:
write_text(json["revert_msg"], origin,
comment="Dying message of tool \"{}\"".format(name))
# if "message" in item:
# writestr(outfile, item["message"], format_strings=True,
# comment="Message for {} '{}'".format(object_type, name),
# **kwargs)
# wrote = True
# if "messages" in item:
# for message in item["messages"]:
# writestr(outfile, message, **kwargs)
# wrote = True
# if "valid_mod_locations" in item:
# for mod_loc in item["valid_mod_locations"]:
# writestr(outfile, mod_loc[0], **kwargs)
# wrote = True

View File

@ -0,0 +1,45 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_gun(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Name of a gun", plural=True)
elif "id" in json:
name = json["id"]
if "description" in json:
write_text(json["description"], origin,
comment="Description of gun \"{}\"".format(name))
if "variants" in json:
for variant in json["variants"]:
variant_name = get_singular_name(variant["name"])
write_text(variant["name"], origin,
comment="Variant name of gun \"{}\"".format(name),
plural=True)
write_text(variant["description"], origin,
comment="Description of variant \"{0}\" of gun \"{1}\""
.format(name, variant_name))
if "modes" in json:
for mode in json["modes"]:
write_text(mode[1], origin,
comment="Firing mode of gun \"{}\"".format(name))
if "skill" in json:
if json["skill"] != "archery":
write_text(json["skill"], origin, context="gun_type_type",
comment="Skill associated with gun \"{}\"".format(name))
if "reload_noise" in json:
write_text(json["reload_noise"], origin,
comment="Reload noise of gun \"{}\"".format(name))
if "valid_mod_locations" in json:
for loc in json["valid_mod_locations"]:
write_text(loc[0], origin,
comment="Valid mod location of gun \"{}\""
.format(name))

View File

@ -0,0 +1,25 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_gunmod(json, origin):
write_text(json["name"], origin, comment="Gun mod name", plural=True)
name = get_singular_name(json["name"])
if "description" in json:
write_text(json["description"], origin,
comment="Description of gun mod \"{}\"".format(name))
if "mode_modifier" in json:
for mode in json["mode_modifier"]:
write_text(mode[1], origin,
comment="Firing mode of gun mod \"{}\"".format(name))
if "location" in json:
write_text(json["location"], origin,
comment="Location of gun mod \"{}\"".format(name))
if "mod_targets" in json:
for target in json["mod_targets"]:
write_text(target, origin, context="gun_type_type",
comment="Target of gun mod \"{}\"".format(name))

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_harvest(json, origin):
if "message" in json:
write_text(json["message"], origin, comment="Harvest message")

View File

@ -0,0 +1,13 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_help(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Help menu")
for msg in json.get("messages", []):
write_text(msg, origin,
comment="Help message in menu \"{}\"".format(name))

View File

@ -0,0 +1,7 @@
from ..write_text import write_text
def parse_item_action(json, origin):
if "name" in json:
write_text(json["name"], origin,
comment="Item action name of \"{}\"".format(json["id"]))

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_item_category(json, origin):
if "name" in json:
write_text(json["name"], origin, comment="Item category name")

View File

@ -0,0 +1,13 @@
from ..write_text import write_text
def parse_json_flag(json, origin):
if "info" in json:
write_text(json["info"], origin, comment=[
"Please leave anything in <angle brackets> unchanged.",
"Description of JSON flag \"{}\"".format(json["id"])
])
if "restriction" in json:
write_text(json["restriction"], origin,
comment="Description of restriction of JSON flag \"{}\""
.format(json["id"]))

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_keybinding(json, origin):
if "name" in json:
write_text(json["name"], origin, comment="Key binding name")

View File

@ -0,0 +1,13 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_loot_zone(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Name of loot zone")
if "description" in json:
write_text(json["description"], origin,
comment="Description of loot zone \"{}\"".format(name))

View File

@ -0,0 +1,21 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_magazine(json, origin):
write_text(json["name"], origin, comment="Magazine name", plural=True)
name = get_singular_name(json["name"])
if "description" in json:
write_text(json["description"], origin,
comment="Description of magazine \"{}\"".format(name))
if "variants" in json:
for variant in json["variants"]:
variant_name = get_singular_name(variant["name"])
write_text(variant["name"], origin,
comment="Variant name of magazine \"{}\"".format(name),
plural=True)
write_text(variant["description"], origin,
comment="Description of variant \"{0}\" "
"of magazine \"{1}\"".format(variant_name, name))

View File

@ -0,0 +1,13 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_map_extra(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Name of map extra")
if "description" in json:
write_text(json["description"], origin,
comment="Description of map extra \"{}\"".format(name))

View File

@ -0,0 +1,51 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_mapgen(json, origin):
if "object" not in json:
return
om = ""
if "om_terrain" in json:
if type(json["om_terrain"]) is str:
om = json["om_terrain"]
elif type(json["om_terrain"]) is list:
if len(json["om_terrain"]) == 0:
om = ""
elif type(json["om_terrain"][0]) is str:
om = ", ".join(json["om_terrain"])
elif type(json["om_terrain"][0]) is list:
om = ", ".join(", ".join(i) for i in json["om_terrain"])
for key in ["place_specials", "place_signs"]:
if key in json["object"]:
for sign in json["object"][key]:
if "signage" in sign:
write_text(sign["signage"], origin,
comment="Signage placed on map {}".format(om))
if "signs" in json["object"]:
for sign in json["object"]["signs"]:
if "signage" in json["object"]["signs"][sign]:
write_text(json["object"]["signs"][sign]["signage"], origin,
comment="Signage placed on map {}".format(om))
if "computers" in json["object"]:
for key in json["object"]["computers"]:
com = json["object"]["computers"][key]
com_name = ""
if "name" in com:
com_name = get_singular_name(com["name"])
write_text(
com["name"], origin,
comment="Computer name placed on map {}".format(om))
for opt in com.get("options", []):
if "name" in opt:
write_text(opt["name"], origin,
comment="Interactive menu name in computer "
"\"{}\" placed on map {}".format(com_name, om))
if "access_denied" in com:
write_text(com["access_denied"], origin,
comment="Access denied message on computer \"{}\""
" placed on map {}".format(com_name, om))

View File

@ -0,0 +1,44 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_martial_art(json, origin):
name = get_singular_name(json["name"])
write_text(name, origin, comment="Name of martial art")
if "description" in json:
write_text(json["description"], origin, c_format=False,
comment="Description of martial art \"{}\"".format(name))
if "initiate" in json:
messages = json["initiate"]
if type(messages) is str:
messages = [messages]
for msg in messages:
write_text(msg, origin,
comment="Initiate message of martial art \"{}\"".
format(name))
onhit_buffs = json.get("onhit_buffs", list())
static_buffs = json.get("static_buffs", list())
onmove_buffs = json.get("onmove_buffs", list())
ondodge_buffs = json.get("ondodge_buffs", list())
onattack_buffs = json.get("onattack_buffs", list())
onpause_buffs = json.get("onpause_buffs", list())
onblock_buffs = json.get("onblock_buffs", list())
ongethit_buffs = json.get("ongethit_buffs", list())
onmiss_buffs = json.get("onmiss_buffs", list())
oncrit_buffs = json.get("oncrit_buffs", list())
onkill_buffs = json.get("onkill_buffs", list())
buffs = (onhit_buffs + static_buffs + onmove_buffs + ondodge_buffs +
onattack_buffs + onpause_buffs + onblock_buffs + ongethit_buffs +
onmiss_buffs + oncrit_buffs + onkill_buffs)
for buff in buffs:
buff_name = get_singular_name(buff["name"])
write_text(buff_name, origin,
comment="Buff name of martial art \"{}\"".format(name))
write_text(buff["description"], origin, c_format=False,
comment="Description of buff \"{0}\" in martial art "
"\"{1}\"".format(name, buff_name))

View File

@ -0,0 +1,20 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_material(json, origin):
name = get_singular_name(json["name"])
write_text(name, origin, comment="Name of material")
if "bash_dmg_verb" in json:
write_text(json["bash_dmg_verb"], origin,
comment="Bash damage verb of material {}".format(name))
if "cut_dmg_verb" in json:
write_text(json["cut_dmg_verb"], origin,
comment="Cut damage verb of material {}".format(name))
if "dmg_adj" in json:
for i in range(4):
write_text(json["dmg_adj"][i], origin,
comment="Damage adjective of material {}".format(name))

View File

@ -0,0 +1,24 @@
from ..helper import get_singular_name
from ..write_text import write_text
from .effect import parse_effect
def parse_mission_definition(json, origin):
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Mission name")
if "description" in json:
write_text(json["description"], origin,
comment="Description of mission \"{}\"".format(name))
if "dialogue" in json:
for key in ["describe", "offer", "accepted", "rejected", "advice",
"inquire", "success", "success_lie", "failure"]:
if key in json["dialogue"]:
write_text(json["dialogue"][key], origin,
comment="Dialogue line in mission \"{}\"".
format(name))
for key in ["start", "end", "fail"]:
if key in json and "effect" in json[key]:
parse_effect(json[key]["effect"], origin)

View File

@ -0,0 +1,9 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_mod_info(json, origin):
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="MOD name", c_format=False)
write_text(json["description"], origin, c_format=False,
comment="Description of MOD \"{}\"".format(name))

View File

@ -0,0 +1,46 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_monster_concrete(json, origin, name):
if "description" in json:
write_text(json["description"], origin, c_format=False,
comment="Description of monster \"{}\"".format(name))
if "death_function" in json:
if "message" in json["death_function"]:
write_text(json["death_function"]["message"], origin,
comment="Death message of monster \"{}\"".format(name))
if "special_attacks" in json:
for attack in json["special_attacks"]:
if "description" in attack:
write_text(attack["description"], origin,
comment="Description of special attack of monster "
"\"{}\"".format(name))
if "monster_message" in attack:
write_text(attack["monster_message"], origin,
comment="Monster message of special attack of "
"monster \"{}\"".format(name))
if "targeting_sound" in attack:
write_text(attack["targeting_sound"], origin,
comment="Targeting sound of special attack of "
"monster \"{}\"".format(name))
if "no_ammo_sound" in attack:
write_text(attack["no_ammo_sound"], origin,
comment="No ammo sound of special attack of "
"monster \"{}\"".format(name))
def parse_monster(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Monster name", plural=True)
elif "id" in json:
name = json["id"]
parse_monster_concrete(json, origin, name)
if "extend" in json:
parse_monster_concrete(json["extend"], origin, name)

View File

@ -0,0 +1,7 @@
from ..write_text import write_text
def parse_monster_attack(json, origin):
for key in ["hit_dmg_u", "hit_dmg_npc", "no_dmg_msg_u", "no_dmg_msg_npc"]:
if key in json:
write_text(json[key], origin, comment="Monster attack message")

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_morale_type(json, origin):
if "text" in json:
write_text(json["text"], origin, comment="Morale text")

View File

@ -0,0 +1,34 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_movement_mode(json, origin):
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Movement mode name")
write_text(json["character"], origin,
comment="Character displayed in the move menu for "
"movement mode \"{}\"".format(name))
write_text(json["panel_char"], origin,
comment="Character displayed in the panel for "
"movement mode \"{}\"".format(name))
write_text(json["change_good_none"], origin,
comment="Successfully switched to movement mode "
"\"{}\" with no steed".format(name))
write_text(json["change_good_animal"], origin,
comment="Successfully switched to movement mode "
"\"{}\" with animal steed".format(name))
write_text(json["change_good_mech"], origin,
comment="Successfully switched to movement mode "
"\"{}\" with mechanical steed".format(name))
if "change_bad_none" in json:
write_text(json["change_bad_none"], origin,
comment="Failed to switched to movement mode "
"\"{}\" with no steed".format(name))
if "change_bad_animal" in json:
write_text(json["change_bad_animal"], origin,
comment="Failed to switched to movement mode "
"\"{}\" with animal steed".format(name))
if "change_bad_mech" in json:
write_text(json["change_bad_mech"], origin,
comment="Failed to switched to movement mode "
"\"{}\" with mechanical steed".format(name))

View File

@ -0,0 +1,58 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_mutation(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Mutation name")
if "description" in json:
write_text(json["description"], origin, c_format=False,
comment="Description of mutation \"{}\"".format(name))
if "attacks" in json:
attacks = json["attacks"]
if type(attacks) is dict:
attacks = [attacks]
for attack in attacks:
if "attack_text_u" in attack:
write_text(attack["attack_text_u"], origin,
comment="Message when player with mutation "
"\"{}\" attacks".format(name))
if "attack_text_npc" in attack:
write_text(attack["attack_text_npc"], origin,
comment="Message when NPC with mutation "
"\"{}\" attacks".format(name))
if "ranged_mutation" in json:
if "message" in json["ranged_mutation"]:
write_text(json["ranged_mutation"]["message"], origin,
comment="Message when firing ranged attack with "
"mutation \"{}\"".format(name))
if "spawn_item" in json:
if "message" in json["spawn_item"]:
write_text(json["spawn_item"]["message"], origin,
comment="Message when spawning item \"{}\" "
"with mutation \"{}\""
.format(json["spawn_item"]["type"], name))
if "triggers" in json:
for arr in json["triggers"]:
for trigger in arr:
if "msg_on" in trigger and "text" in trigger["msg_on"]:
write_text(trigger["msg_on"]["text"], origin,
comment="Trigger message of mutation \"{}\""
.format(name))
if "msg_off" in trigger and "text" in trigger["msg_off"]:
write_text(trigger["msg_off"]["text"], origin,
comment="Trigger message of mutation \"{}\""
.format(name))
if "transform" in json:
write_text(json["transform"]["msg_transform"], origin,
comment="Message when transforming from mutation "
" \"{}\" to \"{}\""
.format(name, json["transform"]["target"]))

View File

@ -0,0 +1,28 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_mutation_category(json, origin):
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Mutation class name")
for key in ["mutagen_message",
"iv_message",
"iv_sleep_message",
"iv_sound_message",
"junkie_message"
]:
if key in json:
write_text(json[key], origin,
comment="\"{}\" of mutation class \"{}\""
.format(key, name))
if "memorial_message" in json:
write_text(json["memorial_message"], origin,
context="memorial_male",
comment="Memorial message of mutation class "
"\"{}\" for male".format(name))
write_text(json["memorial_message"], origin,
context="memorial_female",
comment="Memorial message of mutation class "
"\"{}\" for female".format(name))

View File

@ -0,0 +1,14 @@
from ..write_text import write_text
def parse_npc(json, origin):
gender = "an"
if "gender" in json:
gender = "a {}".format(json["gender"])
comment = json.get("//", None)
if "name_unique" in json:
write_text(json["name_unique"], origin,
comment=["Unique name of {} NPC".format(gender), comment])
if "name_suffix" in json:
write_text(json["name_suffix"], origin,
comment=["Name suffix of {} NPC".format(gender), comment])

View File

@ -0,0 +1,11 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_npc_class(json, origin):
comment = json.get("//", None)
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment=["Name of an NPC class", comment])
write_text(json["job_description"], origin,
comment=["Job description of \"{}\" NPC class".format(name),
comment])

View File

@ -0,0 +1,12 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_overmap_land_use_code(json, origin):
name = get_singular_name(json.get("name", ""))
if "name" in json:
write_text(json["name"], origin,
comment="Name of an overmap land use code")
write_text(json["detailed_definition"], origin, c_format=False,
comment="Detailed definition of overmap land use code \"{}\""
.format(name))

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_overmap_terrain(json, origin):
if "name" in json:
write_text(json["name"], origin, comment="Overmap terrain name")

View File

@ -0,0 +1,8 @@
from ..write_text import write_text
def parse_palette(json, origin):
if "signs" in json:
for x in sorted(json["signs"]):
write_text(json["signs"][x]["signage"], origin,
comment="Signage in mapgen palette")

View File

@ -0,0 +1,13 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_practice(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Practice name")
if "description" in json:
write_text(json["description"], origin,
comment="Description of practice \"{}\"".format(name))

View File

@ -0,0 +1,23 @@
from ..write_text import write_text
def parse_profession(json, origin):
name_male = ""
name_female = ""
if type(json["name"]) is dict:
name_male = json["name"]["male"]
name_female = json["name"]["female"]
elif type(json["name"]) is str:
name_male = name_female = json["name"]
write_text(name_male, origin, context="profession_male",
comment="Profession name for male")
write_text(json["description"], origin, context="prof_desc_male",
comment="Description of profession \"{}\" for male".
format(name_male))
write_text(name_female, origin, context="profession_female",
comment="Profession name for female")
write_text(json["description"], origin, context="prof_desc_female",
comment="Description of profession \"{}\" for female".
format(name_female))

View File

@ -0,0 +1,13 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_proficiency(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Proficiency name")
if "description" in json:
write_text(json["description"], origin,
comment="Description of proficiency \"{}\"".format(name))

View File

@ -0,0 +1,17 @@
from ..write_text import write_text
def parse_recipe(json, origin):
if "book_learn" in json and type(json["book_learn"]) is dict:
for book in json["book_learn"]:
if "recipe_name" in json["book_learn"][book]:
write_text(json["book_learn"][book]["recipe_name"], origin,
comment="Recipe name learnt from book")
if "description" in json:
write_text(json["description"], origin,
comment="Description of recipe crafting \"{}\""
.format(json["result"]))
if "blueprint_name" in json:
write_text(json["blueprint_name"], origin,
comment="Blueprint name of recipe crafting \"{}\""
.format(json["result"]))

View File

@ -0,0 +1,19 @@
from ..write_text import write_text
def parse_recipe_category(json, origin):
cid = json["id"]
if cid == 'CC_NONCRAFT':
return
cat_name = cid.split("_")[1]
write_text(cat_name, origin, comment="Crafting recipes category name")
for subcat in json.get("recipe_subcategories", []):
if subcat == 'CSC_ALL':
write_text("ALL", origin,
comment="Crafting recipes subcategory \"all\"")
else:
subcat_name = subcat.split('_')[2]
write_text(subcat_name, origin,
comment="Crafting recipes subcategory of \"{}\" cat."
.format(cat_name))

View File

@ -0,0 +1,8 @@
from ..write_text import write_text
def parse_recipe_group(json, origin):
if "recipes" in json:
for recipe in json["recipes"]:
write_text(recipe["description"], origin,
comment="Description for recipe group")

View File

@ -0,0 +1,26 @@
from ..write_text import write_text
def parse_scenario(json, origin):
name = ""
if "name" in json:
name = json["name"]
write_text(name, origin, context="scenario_male",
comment="Scenario name for male")
write_text(name, origin, context="scenario_female",
comment="Scenario name for female")
if name == "":
name = json["id"]
if "description" in json:
write_text(json["description"], origin, context="scen_desc_male",
comment="Description of scenario \"{}\" for male"
.format(name))
write_text(json["description"], origin, context="scen_desc_female",
comment="Description of scenario \"{}\" for female"
.format(name))
if "start_name" in json:
write_text(json["start_name"], origin, context="start_name",
comment="Starting location of scenario \"{}\""
.format(name))

View File

@ -0,0 +1,13 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_skill(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Skill name")
if "description" in json:
write_text(json["description"], origin,
comment="Description of skill \"{}\"".format(name))

View File

@ -0,0 +1,7 @@
from ..write_text import write_text
def parse_skill_display_type(json, origin):
write_text(json["display_string"], origin,
comment="Display string for skill display type \"{}\""
.format(json["id"]))

View File

@ -0,0 +1,14 @@
from ..write_text import write_text
def parse_snippet(json, origin):
text = json["text"]
if type(text) is not list:
text = [text]
c_format = "schizophrenia" in json
for snip in text:
if type(snip) is str:
write_text(snip, origin, comment="Snippet", c_format=c_format)
elif type(snip) is dict:
write_text(snip["text"], origin, comment="Snippet",
c_format=c_format)

View File

@ -0,0 +1,11 @@
from ..write_text import write_text
def parse_species(json, origin):
id = json["id"]
if "description" in json:
write_text(json["description"], origin,
comment="Description of species \"{}\"".format(id))
if "footsteps" in json:
write_text(json["footsteps"], origin,
comment="Foot steps of species \"{}\"".format(id))

View File

@ -0,0 +1,8 @@
from ..write_text import write_text
def parse_speech(json, origin):
speaker = ", ".join(json.get("speaker", []))
if "sound" in json:
write_text(json["sound"], origin, c_format=False,
comment="Speech from speaker {}".format(speaker))

View File

@ -0,0 +1,14 @@
from ..write_text import write_text
def parse_speed_description(json, origin):
for speed in json.get("values", []):
if "descriptions" in speed:
descriptions = []
if type(speed["descriptions"]) is list:
descriptions = speed["descriptions"]
else:
descriptions.append(speed["descriptions"])
for desc in descriptions:
write_text(desc, origin,
comment="Speed description of monsters")

View File

@ -0,0 +1,19 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_spell(json, origin):
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Spell name")
if "description" in json:
write_text(json["description"], origin,
comment="Description of spell \"{}\"".format(name))
if "sound_description" in json:
write_text(json["sound_description"], origin,
comment="Sound description of spell \"{}\"".format(name))
if "message" in json:
write_text(json["message"], origin,
comment="Message of spell \"{}\"".format(name))

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_start_location(json, origin):
write_text(json["name"], origin,
comment="Name of starting location \"{}\"".format(json["id"]))

View File

@ -0,0 +1,9 @@
from ..write_text import write_text
def parse_sub_body_part(json, origin):
write_text(json["name"], origin, comment="Name of sub body part")
if "name_multiple" in json:
write_text(json["name_multiple"], origin,
comment="Name of sub body part")

View File

@ -0,0 +1,105 @@
import itertools
from .effect import parse_effect
from ..write_text import write_text
all_genders = ["f", "m", "n"]
def gender_options(subject):
return [subject + ":" + g for g in all_genders]
dynamic_line_string_keys = [
# from `simple_string_conds` in `condition.h`
"u_male", "u_female", "npc_male", "npc_female",
"has_no_assigned_mission", "has_assigned_mission",
"has_many_assigned_missions", "has_no_available_mission",
"has_available_mission", "has_many_available_missions",
"mission_complete", "mission_incomplete", "mission_has_generic_rewards",
"npc_available", "npc_following", "npc_friend", "npc_hostile",
"npc_train_skills", "npc_train_styles",
"at_safe_space", "is_day", "npc_has_activity", "is_outside", "u_has_camp",
"u_can_stow_weapon", "npc_can_stow_weapon", "u_has_weapon",
"npc_has_weapon", "u_driving", "npc_driving",
"has_pickup_list", "is_by_radio", "has_reason",
# yes/no strings for complex conditions, 'and' list
"yes", "no", "and"
]
def parse_dynamic_line(json, origin, comment=[]):
if type(json) is list:
for line in json:
parse_dynamic_line(line, origin, comment)
elif type(json) is dict:
if "gendered_line" in json:
text = json["gendered_line"]
subjects = json["relevant_genders"]
options = [gender_options(subject) for subject in subjects]
for context_list in itertools.product(*options):
context = " ".join(context_list)
write_text(text, origin, context=context,
comment=comment, c_format=False)
for key in dynamic_line_string_keys:
if key in json:
parse_dynamic_line(json[key], origin, comment=comment)
elif type(json) == str:
write_text(json, origin, comment=comment, c_format=False)
def parse_response(json, origin):
if "text" in json:
write_text(json["text"], origin, c_format=False,
comment="Response to NPC dialogue")
if "truefalsetext" in json:
write_text(json["truefalsetext"]["true"], origin, c_format=False,
comment="Affirmative response to NPC dialogue")
write_text(json["truefalsetext"]["false"], origin, c_format=False,
comment="Negative response to NPC dialogue")
if "success" in json:
parse_response(json["success"], origin)
if "failure" in json:
parse_response(json["failure"], origin)
if "speaker_effect" in json:
speaker_effects = json["speaker_effect"]
if not type(speaker_effects) is list:
speaker_effects = [speaker_effects]
for eff in speaker_effects:
if "effect" in eff:
parse_effect(eff["effect"], origin)
if "effect" in json:
parse_effect(json["effect"], origin)
def parse_talk_topic(json, origin):
if "dynamic_line" in json:
comment = ["NPC dialogue line"]
if "//~" in json:
comment.append(json["//~"])
parse_dynamic_line(json["dynamic_line"], origin, comment=comment)
if "responses" in json:
for response in json["responses"]:
parse_response(response, origin)
if "repeat_responses" in json:
if type(json["repeat_responses"]) is dict:
if "response" in json["repeat_responses"]:
parse_response(json["repeat_responses"]["response"], origin)
elif type(json["repeat_responses"]) is list:
for response in json["repeat_responses"]:
if "response" in response:
parse_response(response["response"], origin)
if "effect" in json:
parse_effect(json["effect"], origin)

View File

@ -0,0 +1,18 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_technique(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Martial technique name")
if "description" in json:
write_text(json["description"], origin, c_format=False,
comment="Description of martial technique \"{}\""
.format(name))
for msg in json.get("messages", []):
write_text(msg, origin,
comment="Message of martial technique \"{}\"".format(name))

View File

@ -0,0 +1,37 @@
from ..write_text import write_text
def transform_result(result):
"""
Canonicalize terrain furniture transform results.
Transform result may be a single string, a list of strings,
or a list of list of string and associated probability weights.
"""
if type(result) is str:
return result
elif type(result) is list:
results = set()
for r in result:
if type(r) is str:
results.add(r)
elif type(r) is list:
results.add(r[0])
return ", ".join(results)
def parse_ter_furn_transform(json, origin):
if "fail_message" in json:
write_text(json["fail_message"], origin,
comment="Failure message of terrain furniture transform")
if "terrain" in json:
for terrain in json["terrain"]:
if "message" in terrain:
write_text(terrain["message"], origin,
comment="Message after transforming to \"{}\"".
format(transform_result(terrain["result"])))
if "furniture" in json:
for furniture in json["furniture"]:
if "message" in furniture:
write_text(furniture["message"], origin,
comment="Message after transforming to \"{}\""
.format(transform_result(furniture["result"])))

View File

@ -0,0 +1,35 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_terrain(json, origin):
name = get_singular_name(json.get("name", json["id"]))
if "name" in json:
write_text(json["name"], origin, comment="Terrain name")
if "description" in json:
write_text(json["description"], origin,
comment="Description of terrain \"{}\"".format(name))
if "bash" in json:
if "sound" in json["bash"]:
write_text(json["bash"]["sound"], origin,
comment="Bashing sound of terrain \"{}\"".format(name))
if "sound_fail" in json["bash"]:
write_text(json["bash"]["sound_fail"], origin,
comment="Bashing failed sound of terrain \"{}\""
.format(name))
if "boltcut" in json:
if "message" in json["boltcut"]:
write_text(json["boltcut"]["message"], origin,
comment="Boltcut message of terrain \"{}\"".
format(name))
if "sound" in json["boltcut"]:
write_text(json["boltcut"]["sound"], origin,
comment="Boltcut sound of terrain \"{}\"".format(name))
if "hacksaw" in json:
if "message" in json["hacksaw"]:
write_text(json["hacksaw"]["message"], origin,
comment="Hacksaw message of terrain \"{}\""
.format(name))
if "sound" in json["hacksaw"]:
write_text(json["hacksaw"]["sound"], origin,
comment="Hacksaw sound of terrain \"{}\"".format(name))

View File

@ -0,0 +1,7 @@
from ..write_text import write_text
def parse_tool_quality(json, origin):
if "name" in json:
write_text(json["name"], origin,
comment="Name of tool quality \"{}\"".format(json["id"]))

View File

@ -0,0 +1,9 @@
from ..write_text import write_text
def parse_trap(json, origin):
write_text(json["name"], origin, comment="Name of a trap")
if "vehicle_data" in json and "sound" in json["vehicle_data"]:
write_text(json["vehicle_data"]["sound"], origin,
comment="Trap-vehicle collision message for trap '{}'"
.format(json["name"]))

View File

@ -0,0 +1,51 @@
from ..write_text import write_text
use_action_msg_keys = [
"activate_msg",
"activation_message",
"auto_extinguish_message",
"bury_question",
"charges_extinguish_message",
"deactive_msg",
"descriptions",
"done_message",
"failure_message",
"friendly_msg",
"gerund",
"holster_msg",
"holster_prompt",
"hostile_msg",
"lacks_fuel_message",
"menu_text",
"message",
"msg",
"need_charges_msg",
"need_fire_msg",
"no_deactivate_msg",
"noise_message",
"non_interactive_msg",
"not_ready_msg",
"out_of_power_msg",
"sound_msg",
"success_message",
"unfold_msg",
"use_message",
"verb",
"voluntary_extinguish_message",
"water_extinguish_message",
]
def parse_use_action(json, origin, item_name):
if type(json) is dict:
for msg_key in use_action_msg_keys:
if msg_key in json:
write_text(json[msg_key], origin,
comment="\"{0}\" action message of item \"{1}\""
.format(msg_key, item_name))
for json_key in json:
parse_use_action(json[json_key], origin, item_name)
elif type(json) is list:
for use_action in json:
parse_use_action(use_action, origin, item_name)

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_vehicle(json, origin):
if "name" in json:
write_text(json["name"], origin, comment="Vehicle name")

View File

@ -0,0 +1,15 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_vehicle_part(json, origin):
name = ""
if "name" in json:
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Vehicle part name")
elif "id" in json:
name = json["id"]
if "description" in json:
write_text(json["description"], origin,
comment="Description of vehicle part \"{}\"".format(name))

View File

@ -0,0 +1,10 @@
from ..helper import get_singular_name
from ..write_text import write_text
def parse_vehicle_part_category(json, origin):
name = get_singular_name(json["name"])
write_text(json["name"], origin, comment="Name of a vehicle part category")
write_text(json["short_name"], origin,
comment="Short name of vehicle part category \"{}\""
.format(name))

View File

@ -0,0 +1,7 @@
from ..write_text import write_text
def parse_vehicle_spawn(json, origin):
for st in json.get("spawn_types", []):
write_text(st.get("description"), origin,
comment="Vehicle Spawn Description")

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_vitamin(json, origin):
if "name" in json:
write_text(json["name"], origin, comment="Vitamin name")

View File

@ -0,0 +1,5 @@
from ..write_text import write_text
def parse_weapon_category(json, origin):
write_text(json["name"], origin, comment="Weapon category name")

View File

@ -0,0 +1,6 @@
from ..write_text import write_text
def parse_weather_type(json, origin):
if "name" in json:
write_text(json["name"], origin, comment="Weather type name")

View File

@ -0,0 +1,12 @@
from ..write_text import write_text
def parse_widget(json, origin):
id = json["id"]
if "label" in json:
write_text(json["label"], origin,
comment="Label of UI widget \"{}\"".format(id))
if "strings" in json:
for string in json["strings"]:
write_text(string, origin,
comment="Text in UI widget \"{}\"".format(id))

View File

@ -0,0 +1,118 @@
import codecs
from datetime import datetime, timezone
import json
import os
import polib
from .message import messages
def deduplciate(comments):
"""Remove duplicate comment lines while preserving order."""
seen = set()
result = []
for comment in comments:
if comment not in seen:
result.append(comment)
seen.add(comment)
return result
def is_unicode(sequence):
hex = "0123456789abcdef"
return sequence[0] == "\\" and sequence[1] == "u" and \
sequence[2] in hex and sequence[3] in hex and \
sequence[4] in hex and sequence[5] in hex
def restore_unicode(string):
i = string.find("\\u")
while i != -1 and i + 5 < len(string):
slice = string[i:i + 6]
if is_unicode(slice):
string = string.replace(slice,
codecs.unicode_escape_decode(slice)[0])
i = string.find("\\u")
return string
def format_msg(prefix, text):
return "{0} {1}".format(prefix, restore_unicode(json.dumps(text)))
def write_pot_header(fp, pkg_name="Cataclysm-DDA"):
tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
time = datetime.now(tzinfo).strftime('%Y-%m-%d %H:%M%z')
print("msgid \"\"", file=fp)
print("msgstr \"\"", file=fp)
print("\"Project-Id-Version: {}\\n\"".format(pkg_name), file=fp)
print("\"POT-Creation-Date: {}\\n\"".format(time), file=fp)
print("\"PO-Revision-Date: {}\\n\"".format(time), file=fp)
print("\"Last-Translator: None\\n\"", file=fp)
print("\"Language-Team: None\\n\"", file=fp)
print("\"Language: en\\n\"", file=fp)
print("\"MIME-Version: 1.0\\n\"", file=fp)
print("\"Content-Type: text/plain; charset=UTF-8\\n\"", file=fp)
print("\"Content-Transfer-Encoding: 8bit\\n\"", file=fp)
print("\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"", file=fp)
print("", file=fp)
def sanitize_plural_colissions(reference):
if not os.path.isfile(reference):
raise Exception("cannot read {}".format(reference))
pofile = polib.pofile(reference)
for entry in pofile.untranslated_entries():
if entry.msgid_plural:
pair = (entry.msgctxt if entry.msgctxt else "", entry.msgid)
if pair in messages:
if len(messages[pair]) == 1:
if messages[pair][0].text_plural == "":
messages[pair][0].text_plural = entry.msgid_plural
def write_to_pot(fp, with_header=True, pkg_name=None, sanitize=None):
if sanitize:
sanitize_plural_colissions(sanitize)
if with_header:
write_pot_header(fp, pkg_name)
for (context, text) in sorted(messages):
comments = []
origins = set()
format_tag = ""
text_plural = ""
for message in messages[(context, text)]:
comments = comments + message.comments
origins.add(message.origin)
if message.format_tag:
format_tag = message.format_tag
if message.text_plural:
text_plural = message.text_plural
origin = " ".join(origins)
# translator comments
for line in deduplciate(comments):
print("#. ~ {}".format(line), file=fp)
# reference
print("#: {}".format(origin), file=fp)
# c-format
if format_tag:
print("#, {}".format(format_tag), file=fp)
# context
if context:
print("msgctxt \"{}\"".format(context), file=fp)
# text
if text_plural:
print(format_msg("msgid", text), file=fp)
print(format_msg("msgid_plural", text_plural), file=fp)
print("msgstr[0] \"\"", file=fp)
print("msgstr[1] \"\"", file=fp)
else:
print(format_msg("msgid", text), file=fp)
print("msgstr \"\"", file=fp)
print("", file=fp)

View File

@ -0,0 +1,70 @@
from .message import Message, messages
def append_comment(comments, new_comment):
if type(new_comment) is str:
return comments + new_comment.split("\n")
elif type(new_comment) is list:
for comment in new_comment:
if comment:
comments = append_comment(comments, comment)
return comments
def write_text(json, origin, context="", comment="",
plural=False, c_format=True):
"""
Record a text for translation.
Parameters:
json: The text in string or JSON dict form
origin (str): Path of JSON source location
context (str): "context" as in GNU gettext
comment: Translation comments in either string form or list of strings
plural (bool): Whether the text should be pluralized
c_format (bool): Whether the text contains C-style format string
"""
if json is None or json == "":
return
comments = append_comment([], comment)
text = ""
text_plural = ""
if type(json) is str:
text = json
if plural:
text_plural = "{}s".format(text)
elif type(json) is dict:
if "//~" in json:
if type(json["//~"]) is str and json["//~"]:
comments = append_comment(comments, json["//~"])
if "ctxt" in json:
if type(json["ctxt"]) is str:
context = json["ctxt"]
if "str" in json:
text = json["str"]
if plural:
if "str_sp" in json:
text = json["str_sp"]
text_plural = json["str_sp"]
elif "str_pl" in json:
text_plural = json["str_pl"]
else:
text_plural = "{}s".format(text)
if not text:
return
format_tag = ""
if "%" in text:
if c_format:
format_tag = "c-format"
else:
format_tag = "no-c-format"
if (context, text) not in messages:
messages[(context, text)] = list()
messages[(context, text)].append(
Message(comments, origin, format_tag, context, text, text_plural))

View File

@ -11,20 +11,12 @@ then
fi
fi
# try to extract translatable strings from .json files
echo "> Extracting strings from json"
if ! lang/extract_json_strings.py
then
echo "Error in extract_json_strings.py. Aborting"
exit 1
fi
# Update cataclysm-dda.pot
echo "> Running xgettext to create .pot file"
echo "> Extracting strings from C++ code"
xgettext --default-domain="cataclysm-dda" \
--add-comments="~" \
--sort-by-file \
--output="lang/po/cataclysm-dda.pot" \
--output="lang/po/gui.pot" \
--keyword="_" \
--keyword="pgettext:1c,2" \
--keyword="n_gettext:1,2" \
@ -36,35 +28,34 @@ xgettext --default-domain="cataclysm-dda" \
--keyword="pl_translation:1,2,2t" \
--keyword="pl_translation:1c,2,3,3t" \
--from-code="UTF-8" \
src/*.cpp src/*.h lang/json/*.py
src/*.cpp src/*.h
if [ $? -ne 0 ]; then
echo "Error in xgettext. Aborting"
echo "Error in extracting strings from C++ code. Aborting."
exit 1
fi
# Fix msgfmt errors
if [ "`head -n1 lang/po/cataclysm-dda.pot`" = "# SOME DESCRIPTIVE TITLE." ]
package="cataclysm-dda"
version=$(grep '^VERSION *= *' Makefile | tr -d [:space:] | cut -f 2 -d '=')
echo "> Extracting strings from JSON"
if ! lang/extract_json_strings.py \
-i data \
-x data/mods/TEST_DATA \
-X data/json/items/book/abstract.json \
-X data/json/npcs/TALK_TEST.json \
-X data/raw/color_templates/no_bright_background.json \
-n "$package $version" \
-r lang/po/gui.pot \
-o lang/po/json.pot
then
echo "> Fixing .pot file headers"
package="cataclysm-dda"
version=$(grep '^VERSION *= *' Makefile | tr -d [:space:] | cut -f 2 -d '=')
pot_file="lang/po/cataclysm-dda.pot"
sed -e "1,6d" \
-e "s/^\"Project-Id-Version:.*\"$/\"Project-Id-Version: $package $version\\\n\"/1" \
-e "/\"Plural-Forms:.*\"$/d" \
-e "s/^\"PO-Revision-Date:.*\"$/\"PO-Revision-Date: $(date +%Y-%m-%d\\\ %H:%M%z)\\\n\"/1" \
-e "s/^\"Last-Translator:.*\"$/\"Last-Translator: None\\\n\"/1" \
-e "s/^\"Language-Team:.*\"$/\"Language-Team: None\\\n\"/1" \
-e "s/^\"Language:.*\"$/\"Language: en\\\n\"/1" \
$pot_file > $pot_file.temp
mv $pot_file.temp $pot_file
echo "Error in extracting strings from JSON. Aborting."
exit 1
fi
# strip line-numbers from the .pot file
echo "> Stripping .pot file from unneeded comments"
if ! lang/strip_line_numbers.py lang/po/cataclysm-dda.pot
then
echo "Error in strip_line_numbers.py. Aborting"
echo "> Merging translation templates"
msgcat -o lang/po/cataclysm-dda.pot --sort-output --use-first lang/po/json.pot lang/po/gui.pot
if [ ! -f lang/po/cataclysm-dda.pot ]; then
echo "Error in merging translatoin templates. Aborting."
exit 1
fi
@ -81,10 +72,10 @@ then
fi
# Final compilation check
echo "> Testing to compile the .pot file"
echo "> Testing to compile translation template"
if ! msgfmt -c -o /dev/null lang/po/cataclysm-dda.pot
then
echo "Updated pot file contain gettext errors. Aborting."
echo "Translation template cannot be compiled. Aborting."
exit 1
fi