перевод на русский
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
-- Generated from ChatGPT WoW localisation source.csv.
|
||||
-- Apply to dc_world. Existing frFR rows are not modified.
|
||||
|
||||
INSERT INTO `creature_text_locale` (`CreatureID`,`GroupID`,`ID`,`Locale`,`Text`) VALUES
|
||||
(92718,0,0,'ruRU','Мне понадобится твоя помощь, чтобы остановить Гул''дана. Освободи остальных Иллидари, поскорее!'),
|
||||
(92980,1,0,'ruRU','Демоны хотят уничтожить наш мир. Мы должны освободить наших союзников.'),
|
||||
(92986,1,0,'ruRU','Объяснения могут подождать. Нужно убить демонов.'),
|
||||
(93011,0,0,'ruRU','Вы слышали лорда Иллидана. Давайте найдем этот краеугольный камень.'),
|
||||
(93011,1,0,'ruRU','С его помощью мы сможем вторгнуться на любую планету Легиона, даже на Аргус.'),
|
||||
(93105,0,0,'ruRU','Мои глаза… Мои глаза!'),
|
||||
(93105,2,0,'ruRU','Довольно! Мой адский колосс тебя раздавит.'),
|
||||
(93117,0,0,'ruRU','Какая боль!'),
|
||||
(93127,0,0,'ruRU','Их главные цели — Паучья крепость, Двигатель душ и Кузница. Слуги Иллидана должны добиться успеха.'),
|
||||
(93127,1,0,'ruRU','Если эти эсминцы не будут уничтожены, мы долго не продержимся!'),
|
||||
(93127,3,0,'ruRU','Я чувствую, как это течет по моим венам. Это преображает мое тело.'),
|
||||
(93127,4,0,'ruRU','Нет… Маннетрель.'),
|
||||
(93221,1,0,'ruRU','Вы не переживёте Адскую Вершину…'),
|
||||
(93759,1,0,'ruRU','Используйте тигель для завершения ритуала.'),
|
||||
(93802,0,0,'ruRU','Придите, дети мои. Насладитесь нашими врагами.'),
|
||||
(93802,2,0,'ruRU','Лорд Саргерас, нет!!!'),
|
||||
(94400,0,0,'ruRU','Я вас всех уничтожу.'),
|
||||
(94400,1,0,'ruRU','Я убью каждого демона на своём пути.'),
|
||||
(95226,1,0,'ruRU','Здесь вы — добыча.'),
|
||||
(95226,3,0,'ruRU','В моей клетке, и даже быстрее.'),
|
||||
(96277,0,0,'ruRU','Я тебя расчленю.'),
|
||||
(96279,1,0,'ruRU','В моей клетке, и даже быстрее.'),
|
||||
(96279,2,0,'ruRU','Так сильно они жаждут попасть в рабство.'),
|
||||
(96402,1,0,'ruRU','Умри в славе битвы!'),
|
||||
(96420,0,0,'ruRU','Нас в десять раз больше. Сможем ли мы действительно их победить?'),
|
||||
(96436,1,0,'ruRU','Они от нас не скроются. Иллидари, атака!'),
|
||||
(96441,1,0,'ruRU','Да будет проклято твое призрачное видение!'),
|
||||
(96494,4,0,'ruRU','Вы смеете нападать на нас здесь?!'),
|
||||
(96652,1,0,'ruRU','Аа ...'),
|
||||
(97034,0,0,'ruRU','Я живу, чтобы служить.'),
|
||||
(97034,1,0,'ruRU','Мой клинок пронзит тебя.'),
|
||||
(97057,0,0,'ruRU','Тебе нравится мой новый меч? Подойди поближе.'),
|
||||
(97057,1,0,'ruRU','Глупые мелочи…'),
|
||||
(97058,0,0,'ruRU','Мой рой насытится твоей душой!'),
|
||||
(97244,0,0,'ruRU','Забирайте краеугольный камень! Давайте направимся к воротам внизу и вернемся в Черный Храм!'),
|
||||
(97370,1,0,'ruRU','От моего огня не останется ничего, кроме костей.'),
|
||||
(97459,0,0,'ruRU','Мои силы иссякли. Я подвел тебя.'),
|
||||
(97459,1,0,'ruRU','Клянусь отдать свою жизнь, если это потребуется.'),
|
||||
(98229,0,0,'ruRU','Активируйте три портала и соберите остальные силы.'),
|
||||
(98229,1,0,'ruRU','Я пробьюсь сквозь демонов и встречусь с тобой у вулкана.'),
|
||||
(98292,0,0,'ruRU','Убейте их всех!'),
|
||||
(98354,0,0,'ruRU','Я иду с тобой. Пойдём убьём ещё нескольких демонов.'),
|
||||
(98459,0,0,'ruRU','Знаете, это я должен быть главным.'),
|
||||
(98483,0,0,'ruRU','Для меня это вкусное, сочное мясо.'),
|
||||
(98497,1,0,'ruRU','Мне скоро принесут еду.'),
|
||||
(98497,3,0,'ruRU','Мои дети приготовят для вас идеальное блюдо.'),
|
||||
(98497,4,0,'ruRU','Победа Легиона неизбежна, дитя моё.'),
|
||||
(98497,5,0,'ruRU','Я сам доставлю тебя к Тиранне.'),
|
||||
(98497,6,0,'ruRU','Грязный маленький эльф. Я чувствую вкус скверны на тебе.'),
|
||||
(98711,0,0,'ruRU','Мать маленьких дьяволят находится в той пещере, прямо там.'),
|
||||
(98986,0,0,'ruRU','Мои развращающие тайны поглотят тебя!'),
|
||||
(98986,1,0,'ruRU','Они крадут наши секреты, моя королева…'),
|
||||
(99045,1,0,'ruRU','Я мог бы в одиночку уничтожить Легион!'),
|
||||
(99631,0,0,'ruRU','Я что, не сплю? Как это возможно?'),
|
||||
(99632,0,0,'ruRU','Сколько лет я потратил впустую в этой камере?'),
|
||||
(99917,1,0,'ruRU','Севис опускает взгляд на сломленного, умирающего мистика.'),
|
||||
(99917,2,0,'ruRU','Встретимся у последнего пешеходного моста.'),
|
||||
(100545,0,0,'ruRU','За лорда Иллидана!'),
|
||||
(103432,0,0,'ruRU','Я тебя расчленю.')
|
||||
ON DUPLICATE KEY UPDATE `Text`=VALUES(`Text`);
|
||||
|
||||
INSERT INTO `creature_text_locale` (`CreatureID`,`GroupID`,`ID`,`Locale`,`Text`)
|
||||
SELECT ct.`CreatureID`,ct.`GroupID`,ct.`ID`,'ruRU',REPLACE(REPLACE(ct.`Text`, 'begins to cast', 'начинает произносить'), '[Carrion Storm]', '[Буря Падали]')
|
||||
FROM `creature_text` ct WHERE ct.`CreatureID`=97058 AND ct.`GroupID`=1 AND ct.`ID`=0
|
||||
ON DUPLICATE KEY UPDATE `Text`=VALUES(`Text`);
|
||||
|
||||
INSERT INTO `creature_text_locale` (`CreatureID`,`GroupID`,`ID`,`Locale`,`Text`)
|
||||
SELECT ct.`CreatureID`,ct.`GroupID`,ct.`ID`,'ruRU',REPLACE(REPLACE(REPLACE(ct.`Text`, 'Nearby', 'Ближайшие'), '[Spider Eggs]', '[Паучьи яйца]'), 'will hatch soon if not destroyed!.', 'скоро вылупятся, если их не уничтожить!')
|
||||
FROM `creature_text` ct WHERE ct.`CreatureID`=97059 AND ct.`GroupID`=2 AND ct.`ID`=0
|
||||
ON DUPLICATE KEY UPDATE `Text`=VALUES(`Text`);
|
||||
|
||||
INSERT INTO `gossip_menu_option_locale` (`MenuId`,`OptionIndex`,`Locale`,`OptionText`,`BoxText`) VALUES
|
||||
(18776,0,'ruRU','Уайтмун, я возьму одну из твоих пораженных гангреной летучих мышей. Мы найдем то, за чем пришли.','')
|
||||
ON DUPLICATE KEY UPDATE `OptionText`=VALUES(`OptionText`);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Build ruRU locale overlays from reviewed French-to-Russian CSV rows."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SOURCE_CSV = ROOT / "tools" / "ChatGPT WoW localisation source.csv"
|
||||
OUT_DIR = ROOT / "sql" / "sylvania" / "locales"
|
||||
INVALID = {"", "Загрузка..."}
|
||||
|
||||
BROADCAST_ROW = re.compile(
|
||||
r"^\((\d+),'frFR','((?:''|[^'])*)','((?:''|[^'])*)',(\d+)\)(?:,|;)$"
|
||||
)
|
||||
TMP_BROADCAST_ROW = re.compile(r"^\((\d+), '((?:''|[^'])*)'\)(?:,|;)$")
|
||||
CREATURE_ROW = re.compile(
|
||||
r"^\((\d+),(\d+),(\d+),'frFR','((?:''|[^'])*)'\)(?:,|;)$"
|
||||
)
|
||||
|
||||
|
||||
def sql_quote(value: str) -> str:
|
||||
return "'" + value.replace("'", "''") + "'"
|
||||
|
||||
|
||||
def translations() -> dict[str, str]:
|
||||
result: dict[str, str] = {}
|
||||
with SOURCE_CSV.open(encoding="utf-8-sig", newline="") as source:
|
||||
for row in csv.DictReader(source):
|
||||
russian = row["russian_text"].strip()
|
||||
if row["kind"] == "literal" and russian not in INVALID:
|
||||
result[row["french_text"]] = russian
|
||||
return result
|
||||
|
||||
|
||||
def build_hotfixes(translated: dict[str, str]) -> int:
|
||||
rows: dict[int, tuple[str, str, int]] = {}
|
||||
dynamic_rows: dict[int, str] = {}
|
||||
source = ROOT / "sql/sylvania/broadcast_text_frFR_client.sql"
|
||||
for line in source.read_text(encoding="utf-8").splitlines():
|
||||
match = BROADCAST_ROW.match(line)
|
||||
if not match:
|
||||
continue
|
||||
entry, male, female, build = match.groups()
|
||||
male_ru = translated.get(male, "") if male else ""
|
||||
female_ru = translated.get(female, "") if female else ""
|
||||
if male_ru or female_ru:
|
||||
rows[int(entry)] = (male_ru, female_ru, int(build))
|
||||
|
||||
source = ROOT / "sql/sylvania/mardum_fr_broadcast.sql"
|
||||
for line in source.read_text(encoding="utf-8").splitlines():
|
||||
match = TMP_BROADCAST_ROW.match(line)
|
||||
if not match or match.group(2) not in translated:
|
||||
continue
|
||||
entry = int(match.group(1))
|
||||
dynamic_rows[entry] = translated[match.group(2)]
|
||||
|
||||
output = OUT_DIR / "ruRU_from_french_hotfixes.sql"
|
||||
with output.open("w", encoding="utf-8", newline="\n") as sql:
|
||||
sql.write("-- Generated from ChatGPT WoW localisation source.csv.\n")
|
||||
sql.write("-- Apply to dc_hotfixes. Existing frFR rows are not modified.\n\n")
|
||||
sql.write("INSERT INTO `broadcast_text_locale` "
|
||||
"(`ID`,`locale`,`Text_lang`,`Text1_lang`,`VerifiedBuild`) VALUES\n")
|
||||
values = []
|
||||
for entry, (male, female, build) in sorted(rows.items()):
|
||||
values.append(
|
||||
f"({entry},'ruRU',{sql_quote(male)},{sql_quote(female)},{build})"
|
||||
)
|
||||
sql.write(",\n".join(values))
|
||||
sql.write("\nON DUPLICATE KEY UPDATE\n")
|
||||
sql.write(" `Text_lang`=IF(VALUES(`Text_lang`)<>'',VALUES(`Text_lang`),`Text_lang`),\n")
|
||||
sql.write(" `Text1_lang`=IF(VALUES(`Text1_lang`)<>'',VALUES(`Text1_lang`),`Text1_lang`),\n")
|
||||
sql.write(" `VerifiedBuild`=VALUES(`VerifiedBuild`);\n")
|
||||
sql.write("\nDROP TEMPORARY TABLE IF EXISTS `tmp_ruru_broadcast`;\n")
|
||||
sql.write("CREATE TEMPORARY TABLE `tmp_ruru_broadcast` "
|
||||
"(`id` INT PRIMARY KEY, `text_ru` TEXT) CHARACTER SET utf8mb4;\n")
|
||||
sql.write("INSERT INTO `tmp_ruru_broadcast` (`id`,`text_ru`) VALUES\n")
|
||||
sql.write(",\n".join(
|
||||
f"({entry},{sql_quote(text)})" for entry, text in sorted(dynamic_rows.items())
|
||||
))
|
||||
sql.write(";\n\nINSERT INTO `broadcast_text_locale` "
|
||||
"(`ID`,`locale`,`Text_lang`,`Text1_lang`,`VerifiedBuild`)\n")
|
||||
sql.write("SELECT r.`id`,'ruRU',\n"
|
||||
" CASE WHEN b.`Text` IS NOT NULL AND b.`Text`<>'' THEN r.`text_ru` ELSE '' END,\n"
|
||||
" CASE WHEN b.`Text1` IS NOT NULL AND b.`Text1`<>'' THEN r.`text_ru` ELSE '' END,0\n"
|
||||
"FROM `tmp_ruru_broadcast` r JOIN `broadcast_text` b ON b.`ID`=r.`id`\n"
|
||||
"ON DUPLICATE KEY UPDATE\n"
|
||||
" `Text_lang`=IF(VALUES(`Text_lang`)<>'',VALUES(`Text_lang`),`Text_lang`),\n"
|
||||
" `Text1_lang`=IF(VALUES(`Text1_lang`)<>'',VALUES(`Text1_lang`),`Text1_lang`),\n"
|
||||
" `VerifiedBuild`=VALUES(`VerifiedBuild`);\n"
|
||||
"DROP TEMPORARY TABLE `tmp_ruru_broadcast`;\n")
|
||||
return len(rows) + len(dynamic_rows)
|
||||
|
||||
|
||||
def build_world(translated: dict[str, str]) -> int:
|
||||
rows: dict[tuple[int, int, int], str] = {}
|
||||
source = ROOT / "sql/sylvania/mardum_fr_creature_text.sql"
|
||||
for line in source.read_text(encoding="utf-8").splitlines():
|
||||
match = CREATURE_ROW.match(line)
|
||||
if not match or match.group(4) not in translated:
|
||||
continue
|
||||
creature, group, entry, french = match.groups()
|
||||
rows[(int(creature), int(group), int(entry))] = translated[french]
|
||||
|
||||
output = OUT_DIR / "ruRU_from_french_world.sql"
|
||||
with output.open("w", encoding="utf-8", newline="\n") as sql:
|
||||
sql.write("-- Generated from ChatGPT WoW localisation source.csv.\n")
|
||||
sql.write("-- Apply to dc_world. Existing frFR rows are not modified.\n\n")
|
||||
sql.write("INSERT INTO `creature_text_locale` "
|
||||
"(`CreatureID`,`GroupID`,`ID`,`Locale`,`Text`) VALUES\n")
|
||||
values = []
|
||||
for (creature, group, entry), text in sorted(rows.items()):
|
||||
values.append(
|
||||
f"({creature},{group},{entry},'ruRU',{sql_quote(text)})"
|
||||
)
|
||||
sql.write(",\n".join(values))
|
||||
sql.write("\nON DUPLICATE KEY UPDATE `Text`=VALUES(`Text`);\n\n")
|
||||
|
||||
# These two source rows are assembled dynamically in the French module.
|
||||
replacements = [
|
||||
(97058, 1, 0, [("begins to cast", "начинает произносить"),
|
||||
("[Carrion Storm]", translated.get("[Tempête de charogne]", "[Буря падали]"))]),
|
||||
(97059, 2, 0, [("Nearby", "Ближайшие"),
|
||||
("[Spider Eggs]", translated.get("[Œufs d’araignée]", "[Паучьи яйца]")),
|
||||
("will hatch soon if not destroyed!.", "скоро вылупятся, если их не уничтожить!")]),
|
||||
]
|
||||
for creature, group, entry, pairs in replacements:
|
||||
expression = "ct.`Text`"
|
||||
for old, new in pairs:
|
||||
expression = f"REPLACE({expression}, {sql_quote(old)}, {sql_quote(new)})"
|
||||
sql.write(
|
||||
"INSERT INTO `creature_text_locale` (`CreatureID`,`GroupID`,`ID`,`Locale`,`Text`)\n"
|
||||
f"SELECT ct.`CreatureID`,ct.`GroupID`,ct.`ID`,'ruRU',{expression}\n"
|
||||
f"FROM `creature_text` ct WHERE ct.`CreatureID`={creature} "
|
||||
f"AND ct.`GroupID`={group} AND ct.`ID`={entry}\n"
|
||||
"ON DUPLICATE KEY UPDATE `Text`=VALUES(`Text`);\n\n"
|
||||
)
|
||||
|
||||
gossip_fr = (
|
||||
"Whitemoon, je vais vous emprunter une de vos chauves-souris gangrenées. "
|
||||
"Nous allons chercher ce pour quoi nous sommes venus."
|
||||
)
|
||||
if gossip_fr in translated:
|
||||
sql.write(
|
||||
"INSERT INTO `gossip_menu_option_locale` "
|
||||
"(`MenuId`,`OptionIndex`,`Locale`,`OptionText`,`BoxText`) VALUES\n"
|
||||
f"(18776,0,'ruRU',{sql_quote(translated[gossip_fr])},'')\n"
|
||||
"ON DUPLICATE KEY UPDATE `OptionText`=VALUES(`OptionText`);\n"
|
||||
)
|
||||
return len(rows) + 3
|
||||
|
||||
|
||||
def main() -> None:
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
translated = translations()
|
||||
hotfixes = build_hotfixes(translated)
|
||||
world = build_world(translated)
|
||||
print(f"hotfixes rows: {hotfixes}")
|
||||
print(f"world rows: {world}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user