Localization more

This commit is contained in:
Charlotte Tezuka
2025-10-12 11:33:09 +02:00
parent 967f74bc45
commit 4c2160ee5f
12 changed files with 523 additions and 20 deletions

View File

@@ -0,0 +1,6 @@
ent-WoundFracture = перелом
ent-WoundHusking = шелушение
ent-WoundBoneDeath = костная смерть

View File

@@ -0,0 +1,24 @@
ent-IVStand = стойка для капельницы
.desc = Стойка с креплением для пакетов.
ent-IVBag = пакет для капельницы
.desc = Позволяет постепенно вводить пациенту в кровь лекарства или новую кровь.
ent-IVBagAmmoniaBlood = { ent-IVBag }
.desc = { ent-IVBag.desc }
ent-IVBagBlood = { ent-IVBag }
.desc = { ent-IVBag.desc }
ent-IVBagCopperBlood = { ent-IVBag }
.desc = { ent-IVBag.desc }
ent-IVBagSap = { ent-IVBag }
.desc = { ent-IVBag.desc }
ent-IVBagSlime = { ent-IVBag }
.desc = { ent-IVBag.desc }
ent-CreateFreezerIVBag = контейнер с кровью
.desc = Содержит пакеты с различной кровью.

View File

@@ -0,0 +1,2 @@
ent-StatusEffectSevereBrainDamage = значительный урон мозгу

View File

@@ -0,0 +1,8 @@
ent-BoneGel = костный гель
.desc = Бутылочка геля для восстановления костей.
ent-BoneSetter = репозитор костей
.desc = Инструмент для манипуляций с открытыми костями и переломами.
ent-VascularRecoupler = восстановитель сосудов
.desc = Инструмент для восстановления и сшивания сосудов. Чаще используется для лапши.

View File

@@ -0,0 +1,6 @@
ent-WoundIncision = открытый разрез
ent-WoundRetracted = раздвинутая кожа
ent-WoundOpenRibCage = открытая грудная клетка

View File

@@ -0,0 +1,12 @@
ent-Splints = шины
.desc = Могут удерживать сломанные кости и конечности пациента при транспортировке. Не забывайте менять резину!
ent-TourniquetOffbrand = жгут
.desc = Применяется для перетягивания конечностей в случае кровотечений. Ложка и зажигалка в подарок!
ent-Suture = шовный материал
.desc = Стерильные швы, используемые для закрытия порезов и ран.
.suffix = Полный
ent-Suture1 = { ent-Suture }
.desc = { ent-Suture.desc }
.suffix = Один

View File

@@ -0,0 +1,10 @@
ent-WoundBruise = ушибы
ent-WoundCutMassive = массивные разрезы
ent-WoundCutSevere = серьёзные разрезы
ent-WoundCutModerate = умеренные разрезы
ent-WoundPunctureMassive = массивные проколы

View File

@@ -1,10 +0,0 @@
ent-Suture = шовный материал
.desc = Стерильные швы, используемые для закрытия порезов и ран.
.suffix = Полный
ent-Suture1 = { ent-Suture }
.desc = { ent-Suture.desc }
.suffix = Один
ent-Splints = Шина
.desc = Шины, способные поддерживать и обездвиживать кости.
ent-TourniquetOffbrand = жгут
.desc = Может быть наложен на пациента для остановки кровотечения, но вызывает синяки при длительном использовании.

View File

@@ -1,6 +0,0 @@
ent-BoneGel = флакон костного геля
.desc = { ent-BaseToolSurgery.desc }
ent-BoneSetter = Костный репозитор
.desc = { ent-BaseToolSurgery.desc }
ent-VascularRecoupler = Сосудистый рекапплер
.desc = { ent-BaseToolSurgery.desc }

View File

@@ -1,13 +1,13 @@
cpr-target-needs-cpr = [color=red]{ CAPITALIZE(SUBJECT($target)) } { CONJUGATE-HAVE($target) } не имеет пульса и задыхается![/color]
fracture-examine = [color=red]{ CAPITALIZE(SUBJECT($target)) } { CONJUGATE-BASIC($target, "выглядит", "выглядит") } будто что-то неправильной формы застряло { POSS-ADJ($target) } под кожей![/color]
arterial-bleeding-examine = [color=red]{ CAPITALIZE(SUBJECT($target)) } фантанирует кровью![/color]
arterial-bleeding-examine = [color=red]{ CAPITALIZE(SUBJECT($target)) } фонтанирует кровью![/color]
bone-death-examine = [color=red]{ CAPITALIZE(SUBJECT($target)) } искалечен![/color]
wound-bleeding-modifier = [color=red]кровоточащие {$wound}[/color]
wound-tended-modifier = заботиться {$wound}
wound-bandaged-modifier = имеет перевязку {$wound}
wound-salved-modifier = спасено {$wound}
wound-tended-modifier = ухоженные {$wound}
wound-bandaged-modifier = перевязанные {$wound}
wound-salved-modifier = помазанные {$wound}
tourniquet-applied-examine = { CAPITALIZE(SUBJECT($target)) } { CONJUGATE-HAVE($target) } применяет турникет на { OBJECT($target) }.
splints-applied-examine = { CAPITALIZE(SUBJECT($target)) } { CONJUGATE-HAVE($target) } накладывает шину { OBJECT($target) }.

448
convert_yml_to_ftl_full.py Normal file
View File

@@ -0,0 +1,448 @@
# convert_yml_to_ftl.py
import yaml
import os
import sys
import argparse
import select # Для Linux/Mac
from pathlib import Path
def wait_for_key_press():
"""
Ожидает нажатия любой клавиши перед закрытием терминала
"""
print("\n\nНажмите любую клавишу для выхода...")
if os.name == 'nt': # Windows
msvcrt.getch()
else: # Linux/Mac
if select.select([sys.stdin], [], [], 0)[0]:
sys.stdin.read(1)
else:
# Если stdin не готов, используем input
input()
def find_all_prototype_files(prototypes_root="Resources/Prototypes"):
"""
Находит все YAML файлы прототипов в директории Resources/Prototypes/
"""
prototype_files = []
if not os.path.exists(prototypes_root):
print(f"⚠️ Директория {prototypes_root} не найдена")
return prototype_files
for root, dirs, files in os.walk(prototypes_root):
for file in files:
if file.endswith('.yml') or file.endswith('.yaml'):
full_path = os.path.join(root, file)
prototype_files.append(full_path)
print(f"📁 Найдено {len(prototype_files)} YAML файлов в {prototypes_root}")
return prototype_files
def build_global_prototype_map(prototypes_root="Resources/Prototypes"):
"""
Строит глобальную карту всех прототипов из всех файлов в Resources/Prototypes/
"""
prototype_files = find_all_prototype_files(prototypes_root)
global_prototype_map = {}
for file_path in prototype_files:
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
if not data:
continue
for item in data:
if item and 'id' in item and item.get('type', '').lower() == 'entity':
global_prototype_map[item['id']] = item
except Exception as e:
print(f"⚠️ Ошибка при чтении файла {file_path}: {e}")
print(f"🗂️ Загружено {len(global_prototype_map)} entity прототипов из всех файлов")
return global_prototype_map
def get_field_recursive(prototype_map, prototype_id, field_name):
"""
Рекурсивно ищет поле в прототипе и его родителях
"""
if prototype_id not in prototype_map:
return None
current_prototype = prototype_map[prototype_id]
# Проверяем текущий прототип
if field_name in current_prototype and current_prototype[field_name]:
return current_prototype[field_name]
# Если поля нет, проверяем родителя
parent_id = current_prototype.get('parent')
if parent_id and parent_id in prototype_map:
return get_field_recursive(prototype_map, parent_id, field_name)
return None
def format_field_value(value):
"""
Форматирует значение поля для FTL файла
Если значение - строка, возвращает как есть (без кавычек)
"""
if isinstance(value, str):
return value
elif isinstance(value, (int, float)):
return str(value)
else:
return str(value)
def get_input_files():
"""
Запрашивает у пользователя путь к файлу или папке для обработки
"""
print("\n" + "="*60)
print("📁 ВЫБОР ФАЙЛОВ ДЛЯ ЛОКАЛИЗАЦИИ")
print("="*60)
while True:
print("\nВыберите вариант:")
print("1. Обработать один конкретный YAML файл")
print("2. Обработать все YAML файлы в папке")
print("3. Обработать все YAML файлы в Resources/Prototypes/")
choice = input("\nВведите номер варианта (1-3): ").strip()
if choice == "1":
return get_single_file()
elif choice == "2":
return get_folder_files()
elif choice == "3":
return get_all_prototype_files()
else:
print("❌ Неверный выбор. Попробуйте снова.")
def get_single_file():
"""
Запрашивает путь к одному конкретному файлу
"""
while True:
file_path = input("\nВведите путь к YAML файлу: ").strip()
if not file_path:
print("❌ Путь не может быть пустым!")
continue
if not os.path.exists(file_path):
print("❌ Файл не существует!")
continue
if not (file_path.endswith('.yml') or file_path.endswith('.yaml')):
print("❌ Файл должен быть YAML (.yml или .yaml)!")
continue
return [file_path]
def get_folder_files():
"""
Запрашивает путь к папке и находит все YAML файлы в ней
"""
while True:
folder_path = input("\nВведите путь к папке: ").strip()
if not folder_path:
print("❌ Путь не может быть пустым!")
continue
if not os.path.exists(folder_path):
print("❌ Папка не существует!")
continue
if not os.path.isdir(folder_path):
print("❌ Это не папка!")
continue
# Находим все YAML файлы в папке
yaml_files = []
for file in os.listdir(folder_path):
if file.endswith('.yml') or file.endswith('.yaml'):
yaml_files.append(os.path.join(folder_path, file))
if not yaml_files:
print("В папке не найдено YAML файлов!")
continue
print(f"✅ Найдено {len(yaml_files)} YAML файлов:")
for file in yaml_files:
print(f" - {os.path.basename(file)}")
confirm = input("\nПродолжить с этими файлами? (y/n): ").strip().lower()
if confirm in ['y', 'yes', 'д', 'да']:
return yaml_files
else:
continue
def get_all_prototype_files():
"""
Возвращает все файлы из Resources/Prototypes/
"""
prototype_files = find_all_prototype_files()
if not prototype_files:
print("В Resources/Prototypes/ не найдено YAML файлов!")
return []
print(f"✅ Найдено {len(prototype_files)} YAML файлов в Resources/Prototypes/")
confirm = input("\nПродолжить со всеми файлами? (y/n): ").strip().lower()
if confirm in ['y', 'yes', 'д', 'да']:
return prototype_files
else:
return []
def get_output_location(input_files):
"""
Запрашивает у пользователя куда сохранять FTL файлы
"""
print("\n" + "="*60)
print("📂 ВЫБОР МЕСТА СОХРАНЕНИЯ")
print("="*60)
while True:
print("\nВыберите вариант сохранения:")
print("1. Сохранить в указанную папку (сохранит структуру папок)")
print("2. Сохранить все в одну папку")
choice = input("\nВведите номер варианта (1-2): ").strip()
if choice == "1":
return get_structured_output()
elif choice == "2":
return get_flat_output()
else:
print("❌ Неверный выбор. Попробуйте снова.")
def get_structured_output():
"""
Запрашивает корневую папку для сохранения с сохранением структуры
"""
while True:
output_root = input("\nВведите корневую папку для FTL файлов (например: Resources/Locale/ru): ").strip()
if not output_root:
print("❌ Путь не может быть пустым!")
continue
# Создаем папку если её нет
try:
os.makedirs(output_root, exist_ok=True)
return output_root, "structured"
except Exception as e:
print(f"❌ Ошибка создания папки: {e}")
def get_flat_output():
"""
Запрашивает одну папку для сохранения всех FTL файлов
"""
while True:
output_folder = input("\nВведите папку для FTL файлов: ").strip()
if not output_folder:
print("❌ Путь не может быть пустым!")
continue
# Создаем папку если её нет
try:
os.makedirs(output_folder, exist_ok=True)
return output_folder, "flat"
except Exception as e:
print(f"❌ Ошибка создания папки: {e}")
def convert_single_file(yml_path, ftl_path, global_prototype_map):
"""
Конвертирует один конкретный YAML файл в FTL с рекурсивным поиском полей
"""
try:
# Читаем YAML как сырой текст, чтобы определить уровень вложенности
with open(yml_path, 'r', encoding='utf-8') as yf:
raw_lines = yf.readlines()
# Парсим YAML для получения структуры данных
with open(yml_path, 'r', encoding='utf-8') as yf:
data = yaml.safe_load(yf)
# Создаем директорию для FTL файла если её нет
ftl_dir = os.path.dirname(ftl_path)
if ftl_dir and not os.path.exists(ftl_dir):
os.makedirs(ftl_dir)
# Анализируем отступы чтобы определить корневые элементы
root_items = []
current_item = None
current_indent = 0
for line in raw_lines:
stripped_line = line.strip()
if not stripped_line or stripped_line.startswith('#'):
continue
# Определяем уровень отступа
indent_level = len(line) - len(line.lstrip())
if line.lstrip().startswith('- '):
# Новый элемент списка
if current_item is not None:
root_items.append(current_item)
current_item = {'line': line, 'indent': indent_level, 'is_root': True}
elif current_item is not None and indent_level > current_item['indent']:
# Вложенный элемент - помечаем как не корневой
current_item['is_root'] = False
elif current_item is not None and indent_level <= current_item['indent']:
# Завершаем текущий элемент и начинаем новый
root_items.append(current_item)
if line.lstrip().startswith('- '):
current_item = {'line': line, 'indent': indent_level, 'is_root': True}
else:
current_item = None
# Добавляем последний элемент
if current_item is not None:
root_items.append(current_item)
# Записываем FTL
count = 0
skipped_count = 0
with open(ftl_path, 'w', encoding='utf-8') as ff:
for i, item in enumerate(data):
if not item or 'id' not in item:
continue
# Проверяем, является ли элемент корневым
is_root_element = i < len(root_items) and root_items[i]['is_root']
# Пропускаем если не entity ИЛИ если это вложенный элемент
if not is_root_element or item.get('type', '').lower() != 'entity':
skipped_count += 1
continue
prototype_id = format_field_value(item['id'])
# Основная строка с ID и именем
name = get_field_recursive(global_prototype_map, prototype_id, 'name')
if not name:
name = item.get('name', '')
if name:
ff.write(f"ent-{prototype_id} = {format_field_value(name)}\n")
else:
ff.write(f"ent-{prototype_id} = \n")
# Проверяем локально
desc = get_field_recursive(global_prototype_map, prototype_id, 'desc')
if 'desc' in item and item['desc']:
ff.write(f" .desc = {format_field_value(item['desc'])}\n")
# Если не нашли локально, рекурсивно ищем desc
elif desc:
ff.write(f" .desc = {format_field_value(desc)}\n")
# проверяем локально
suffix = get_field_recursive(global_prototype_map, prototype_id, 'suffix')
if 'suffix' in item and item['suffix']:
ff.write(f" .suffix = {format_field_value(item['suffix'])}\n")
# Если не нашли локально, рекурсивно ищем suffix
elif suffix:
ff.write(f" .suffix = {format_field_value(suffix)}\n")
# Пустая строка между записями для читаемости
ff.write("\n")
count += 1
print(f"✓ Конвертирован: {os.path.basename(yml_path)} -> {ftl_path}")
return count, skipped_count
except Exception as e:
print(f"✗ Ошибка при конвертации {yml_path}: {e}")
return 0, 0
def process_files(input_files, output_location, output_type, global_prototype_map):
"""
Обрабатывает все выбранные файлы
"""
print("\n" + "="*60)
print("🔄 НАЧАЛО ЛОКАЛИЗАЦИИ")
print("="*60)
total_entities = 0
total_skipped = 0
processed_files = 0
for input_file in input_files:
# Определяем путь для выходного файла
if output_type == "structured":
# Сохраняем структуру папок относительно Resources/Prototypes/
relative_path = Path(input_file).relative_to("Resources/Prototypes")
ftl_path = Path(output_location) / relative_path.with_suffix('.ftl')
else:
# flat - сохраняем все в одну папку
filename = Path(input_file).with_suffix('.ftl').name
ftl_path = Path(output_location) / filename
# Конвертируем файл
entities, skipped = convert_single_file(input_file, str(ftl_path), global_prototype_map)
total_entities += entities
total_skipped += skipped
processed_files += 1
print(f"\n📊 ИТОГИ ЛОКАЛИЗАЦИИ:")
print(f" Обработано файлов: {processed_files}")
print(f" Добавлено entity: {total_entities}")
print(f" Пропущено не-entity: {total_skipped}")
print(f" Файлы сохранены в: {output_location}")
def main_interactive():
"""
Основная функция с интерактивным интерфейсом
"""
print("🚀 КОНВЕРТЕР YAML В FTL ДЛЯ SPACE STATION 14")
print("="*60)
# Шаг 1: Выбор файлов для обработки
input_files = get_input_files()
if not input_files:
print("Не выбрано файлов для обработки!")
return False
# Шаг 2: Выбор места сохранения
output_location, output_type = get_output_location(input_files)
# Шаг 3: Загрузка глобальной карты прототипов
print("\n🔄 Загрузка всех прототипов для поиска parent'ов...")
global_prototype_map = build_global_prototype_map()
if not global_prototype_map:
print("Не удалось загрузить прототипы!")
return False
# Шаг 4: Обработка файлов
process_files(input_files, output_location, output_type, global_prototype_map)
return True
if __name__ == "__main__":
try:
# Всегда используем интерактивный режим
success = main_interactive()
if success:
print("\n✅ Локализация завершена успешно!")
else:
print("\n❌ Локализация завершена с ошибками!")
except KeyboardInterrupt:
print("\n\n⏹️ Операция прервана пользователем")
except Exception as e:
print(f"\n💥 Критическая ошибка: {e}")
finally:
# Ожидаем нажатия клавиши перед закрытием
wait_for_key_press()

View File

@@ -43,6 +43,9 @@ let
at-spi2-core
cups
python3
python313Packages.pyyaml
rPackages.argparse
];
in pkgs.mkShell {
name = "space-station-14-devshell";