-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
update nether deer, add unposter, allow juicing mc mushrooms, book setup
- Loading branch information
1 parent
d017d84
commit 523e833
Showing
85 changed files
with
2,077 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import difflib | ||
import json | ||
|
||
from typing import Tuple | ||
|
||
|
||
def main(validate: bool, namespace: str, langs: Tuple[str, ...]): | ||
en_us = load(namespace, 'en_us') | ||
for lang in langs: | ||
if lang != 'en_us': | ||
format_lang(namespace, en_us, lang, validate) | ||
|
||
|
||
def update(namespace: str, langs: Tuple[str, ...]): | ||
en_us = load(namespace, 'en_us') | ||
en_us_old = load_old(namespace, 'en_us') | ||
updated_keys = {k for k in en_us.keys() if k in en_us_old and en_us[k] != en_us_old[k]} | ||
|
||
if updated_keys: | ||
print('Found %d modified values:' % len(updated_keys)) | ||
for k in updated_keys: | ||
print('Modified: %s : "%s" -> "%s"' % (k, en_us_old[k], en_us[k])) | ||
|
||
inp = input('Remove these keys from other translations?\n(yes|no) >') | ||
print('Answer: %s' % inp) | ||
if inp == 'yes': | ||
# Strip these keys from en_us, so they don't show up in translations | ||
for k in updated_keys: | ||
del en_us[k] | ||
for lang in langs: | ||
if lang != 'en_us': | ||
format_lang(namespace, en_us, lang, False) | ||
else: | ||
print('No differences found') | ||
|
||
|
||
def format_lang(namespace: str, en_us, lang: str, validate: bool): | ||
lang_data = load(namespace, lang) | ||
lang_comments = {k: v for k, v in lang_data.items() if '__comment' in k and v != 'This file was automatically created by mcresources'} | ||
lang_data = {k: v for k, v in lang_data.items() if '__comment' not in k} | ||
|
||
formatted_lang_data = {} | ||
for k, v in lang_comments.items(): | ||
formatted_lang_data[k] = v | ||
|
||
translated = 0 | ||
for k, v in en_us.items(): | ||
if '__comment' in k: | ||
pass # Exclude comments in en_us | ||
elif k in lang_data and lang_data[k] != v: | ||
translated += 1 | ||
formatted_lang_data[k] = lang_data[k] | ||
else: | ||
formatted_lang_data[k] = v | ||
|
||
# Unique keys to this language, only allowed in the default vanilla overrides. It makes no sense for a language to have uniquely named TFC keys | ||
# But, for vanilla minecraft, we may have to override for vanilla items we rename without renaming. | ||
# e.g. we use 'Egg' but if a translation is 'Chicken Egg', that might be renamed for other languages only. | ||
if namespace == 'minecraft': | ||
for k, v in lang_data.items(): | ||
if k not in en_us: | ||
formatted_lang_data[k] = v | ||
|
||
print('Translation progress for %s (%s): %d / %d (%.1f%%)' % (lang, namespace, translated, len(en_us), 100 * translated / len(en_us))) | ||
save(namespace, lang, formatted_lang_data, validate) | ||
|
||
|
||
def load(namespace: str, lang: str): | ||
with open('./src/main/resources/assets/%s/lang/%s.json' % (namespace, lang), 'r', encoding='utf-8') as f: | ||
return json.load(f) | ||
|
||
|
||
def load_old(namespace: str, lang: str): | ||
""" The old lang file need to be manually placed under the project root and | ||
be named as exactly `<lang>.<namespace>.old.json`, where <lang> is the | ||
language code, and <namespace> is usually either 'minecraft' or 'tfc'. | ||
""" | ||
with open('./%s.%s.old.json' % (lang, namespace), 'r', encoding='utf-8') as f: | ||
return json.load(f) | ||
|
||
|
||
def save(namespace: str, lang: str, lang_data, validate: bool): | ||
if validate: | ||
with open('./src/main/resources/assets/%s/lang/%s.json' % (namespace, lang), 'r', encoding='utf-8') as f: | ||
old_lang_data = json.load(f) | ||
assert old_lang_data == lang_data, 'Validation error in mod localization for %s:\n\n=== Diff (expected vs. actual) ===\n\n%s' % (lang, '\n'.join(difflib.unified_diff(json.dumps(lang_data, ensure_ascii=False, indent=2).split('\n'), json.dumps(old_lang_data, ensure_ascii=False, indent=2).split('\n')))) | ||
else: | ||
with open('./src/main/resources/assets/%s/lang/%s.json' % (namespace, lang), 'w', encoding='utf-8') as f: | ||
json.dump(lang_data, f, ensure_ascii=False, indent=2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import format_lang | ||
from patchouli import * | ||
from argparse import ArgumentParser | ||
from typing import Optional | ||
|
||
BOOK_LANGUAGES = ('en_us',) | ||
MOD_LANGUAGES = ('en_us',) | ||
|
||
class LocalInstance: | ||
INSTANCE_DIR = None | ||
|
||
@staticmethod | ||
def wrap(rm: ResourceManager): | ||
def data(name_parts: ResourceIdentifier, data_in: JsonObject, root_domain: str = 'data'): | ||
return rm.write((LocalInstance.INSTANCE_DIR, '/'.join(utils.str_path(name_parts))), data_in) | ||
|
||
if LocalInstance.INSTANCE_DIR is not None: | ||
rm.data = data | ||
return rm | ||
return None | ||
|
||
def main_with_args(): | ||
parser = ArgumentParser('generate_book.py') | ||
parser.add_argument('--translate', type=str, default='en_us', help='The language to translate to') | ||
parser.add_argument('--local', type=str, default=None, help='The directory of a local .minecraft to copy into') | ||
parser.add_argument('--translate-all', type=str, default=None, help='If all languages should be translated') | ||
parser.add_argument('--format', type=str, default=None, help='Format the mod languages') | ||
parser.add_argument('--reverse-translate', type=str, default=None, help='Reverse a translation from the mod files.') | ||
|
||
args = parser.parse_args() | ||
|
||
if args.format: | ||
do_format() | ||
return | ||
|
||
if args.translate_all: | ||
do_format() | ||
for la in BOOK_LANGUAGES: | ||
main(la, args.local, False, reverse_translate=args.reverse_translate is not None) | ||
else: | ||
main(args.translate, args.local, False, reverse_translate=args.reverse_translate is not None) | ||
|
||
def do_format(): | ||
# format_lang.main(False, 'minecraft', BOOK_LANGUAGES) | ||
format_lang.main(False, 'beneath', MOD_LANGUAGES) | ||
|
||
def main(translate_lang: str, local_minecraft_dir: Optional[str], validate: bool, validating_rm: ResourceManager = None, reverse_translate: bool = False): | ||
LocalInstance.INSTANCE_DIR = local_minecraft_dir | ||
|
||
rm = ResourceManager('tfc', './src/main/resources') | ||
if validate: | ||
rm = validating_rm | ||
i18n = I18n(translate_lang, validate) | ||
|
||
print('Writing book at %s' % translate_lang) | ||
make_book(rm, i18n, local_instance=False, reverse_translate=reverse_translate) | ||
|
||
i18n.flush() | ||
|
||
if LocalInstance.wrap(rm): | ||
print('Copying %s book into local instance at: %s' % (translate_lang, LocalInstance.INSTANCE_DIR)) | ||
make_book(rm, I18n(translate_lang, validate), local_instance=True) | ||
|
||
|
||
# def main(): | ||
# for language in BOOK_LANGUAGES: | ||
# rm = ResourceManager('tfc', '../src/main/resources') | ||
# i18n = I18n.create(language) | ||
# | ||
# print('Writing book %s' % language) | ||
# make_book(rm, i18n) | ||
# | ||
# i18n.flush() | ||
# | ||
# if LocalInstance.wrap(rm) and language == 'en_us': | ||
# print('Copying into local instance at: %s' % LocalInstance.INSTANCE_DIR) | ||
# make_book(rm, I18n.create('en_us'), local_instance=True) | ||
# | ||
# print('Done') | ||
|
||
def make_book(rm: ResourceManager, i18n: I18n, local_instance: bool = False, reverse_translate: bool = False): | ||
book = Book(rm, 'field_guide', {}, i18n, local_instance, reverse_translate) | ||
|
||
book.category('beneath', 'Beneath', 'All about what is Beneath', 'beneath:cursecoal', is_sorted=True, entries=( | ||
entry('beneath', 'What Lies Beneath', 'beneath:textures/item/cursecoal.png', pages=( | ||
text('XXX'), | ||
empty_last_page() | ||
)), | ||
)) | ||
|
||
book.build() | ||
|
||
# beneath Pages | ||
|
||
def knapping(recipe: str, text_content: TranslatableStr) -> Page: return recipe_page('knapping_recipe', recipe, text_content) | ||
|
||
def alloy_recipe(title: str, ingot: str, *components: Tuple[str, int, int], text_content: str) -> Page: | ||
recipe = ''.join(['$(li)%d - %d %% : $(thing)%s$()' % (lo, hi, alloy) for (alloy, lo, hi) in components]) | ||
return item_spotlight(ingot, title, False, '$(br)$(bold)Requirements:$()$(br)' + recipe + '$(br2)' + text_content) | ||
|
||
def custom_component(x: int, y: int, class_name: str, data: JsonObject) -> Component: | ||
return Component('patchouli:custom', x, y, {'class': 'com.eerussianguy.beneath.compat.patchouli.' + class_name, **data}) | ||
|
||
|
||
if __name__ == '__main__': | ||
main_with_args() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import json | ||
import os | ||
|
||
import Levenshtein | ||
|
||
|
||
class I18n: | ||
|
||
lang: str | ||
|
||
def __init__(self, lang: str, validate: bool = False): | ||
self.lang = lang | ||
self.before = {} | ||
self.after = {} | ||
self.validate = validate | ||
self.lang_path = './resources/lang/%s.json' % lang | ||
|
||
self.fuzzy_matches = 0 | ||
self.fuzzy_non_matches = 0 | ||
|
||
# Default translation | ||
if not os.path.isfile(self.lang_path): | ||
if validate: | ||
raise ValueError('Cannot validate book for lang %s, as resources/lang/%s.json does not exist' % (lang, lang)) | ||
print('Writing default translation for language %s to %s' % (self.lang, self.lang_path)) | ||
with open(self.lang_path, 'w', encoding='utf-8') as f: | ||
f.write('{}\n') | ||
|
||
# Read the existing translation | ||
with open(self.lang_path, 'r', encoding='utf-8') as f: | ||
print('Reading translation for language %s to %s' % (self.lang, self.lang_path)) | ||
j = json.load(f) | ||
|
||
# Parse json | ||
for key, value in j.items(): | ||
if not isinstance(value, str): | ||
print('Illegal translation entry: "%s": "%s"' % (key, value)) | ||
exit(-1) | ||
self.before[key] = value | ||
|
||
def is_root(self) -> bool: | ||
""" Return true if we are in the root language (en_us) """ | ||
return self.lang == 'en_us' | ||
|
||
def translate(self, text: str) -> str: | ||
""" Translates the string into the current domain """ | ||
if self.is_root(): | ||
# For en_us, always keep the current text (read only) | ||
translated = text | ||
elif text in self.before: | ||
translated = self.before[text] # Translate if available | ||
else: | ||
# Try a fuzzy matcher (if we're not in en_us) | ||
# Use the lowercase of both keys, as difference in capitalization is almost surely not a translation issue | ||
distance, match = min(((Levenshtein.distance(text.lower(), key.lower()), key) for key in self.before.keys())) | ||
if distance / len(text) < 0.1 and distance < 20: # Heuristic: < 5% of text, and < 20 overall distance | ||
if self.before[match] == match: | ||
# This has just matched a default key that was inserted in the translated files | ||
# So if we slightly modify the en_us default, we should change this value as well. | ||
self.fuzzy_non_matches += 1 | ||
translated = text | ||
else: | ||
# Use the fuzzy match | ||
self.fuzzy_matches += 1 | ||
translated = self.before[match] | ||
else: | ||
# Not available, but record and output anyway | ||
self.fuzzy_non_matches += 1 | ||
translated = text | ||
|
||
self.after[text] = translated | ||
return translated | ||
|
||
def flush(self): | ||
""" Updates the local translation file, if needed """ | ||
if not self.is_root() and self.fuzzy_matches + self.fuzzy_non_matches > 0: | ||
print('Matched %d / %d entries (%.1f%%). Updated %d entries for lang %s.' % (self.fuzzy_matches, self.fuzzy_matches + self.fuzzy_non_matches, 100 * self.fuzzy_matches / (self.fuzzy_matches + self.fuzzy_non_matches), self.fuzzy_non_matches, self.lang)) | ||
if self.validate: | ||
assert self.before == self.after, 'Validation error translating book to lang \'%s\'' % self.lang | ||
with open(self.lang_path, 'w', encoding='utf-8') as f: | ||
unique_count = len(self.after) if self.is_root() else sum(k != v for k, v in self.after.items()) | ||
if unique_count > 0: | ||
print('Writing updated translation for language %s: %d / %d (%.2f%%)' % (self.lang, unique_count, len(self.after), 100 * unique_count / len(self.after))) | ||
json.dump(self.after, f, indent=2, ensure_ascii=False) | ||
|
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.