diff --git a/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs b/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs index 493ae2be85..2704dae64f 100644 --- a/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs +++ b/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs @@ -173,7 +173,8 @@ public sealed class SunriseCCVars public static readonly CVarDef LobbyOpacity = CVarDef.Create("lobby.lobby_opacity", 0.90f, CVar.CLIENTONLY | CVar.ARCHIVE); - public static readonly CVarDef ServerName = CVarDef.Create("lobby.server_name", "SS14", CVar.REPLICATED); + public static readonly CVarDef ServerName = + CVarDef.Create("lobby.server_name", "SS14", CVar.SERVER | CVar.REPLICATED); /* * Planet Prison diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/actions/disease.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/actions/disease.ftl new file mode 100644 index 0000000000..8ced54162f --- /dev/null +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/actions/disease.ftl @@ -0,0 +1,32 @@ +ent-ActionInfect = Заразить [10 ОЭ] + .desc = Попытка заразить существо [-10 очков эволюции]. Первые три попытки заражения не требуют очков эволюции. Выберите способность и нажмите ЛКМ по сущности, чтобы заразить. +ent-ActionDiseaseShop = Мутация + .desc = Открывает меню мутации +ent-ActionDiseaseBaseChance = Улучшение заражения. [20 ОЭ] + .desc = Действие 'заразить' имеет больший шанс на успех. [-20 очков эволюции] +ent-ActionDiseaseInfectChance = Повышение заразности. [15 ОЭ] + .desc = Увеличивает заразность вируса. Влияет на шанс заражения. [-15 очков эволюции] +ent-ActionDiseaseShield = Повышение устойчивости. [15 ОЭ] + .desc = Повышает устойчивость болезни к лекарствам. Влияет на скорость лечения при употреблении лекарства. [-15 очков эволюции] +ent-ActionDiseaseLethal = Повышение летальности. [15 ОЭ] + .desc = Увеличивает получаемый урон от симптомов. Влияет на получаемый при кашле урон. [-15 очков эволюции] +ent-ActionDiseaseCough = Кашель + .desc = Заражённые начинают кашлять. +ent-ActionDiseaseSneeze = Чих + .desc = Заражённые начинают чихать. +ent-ActionDiseaseVomit = Тошнота + .desc = Заражённых начинает тоншить, вызывая рвоту. +ent-ActionDiseaseNarcolepsy = Сонливость + .desc = У заражённых появляется постоянное желание спать, с которым они иногда не могут справиться. +ent-ActionDiseaseCrying = Непроизвольные слёзы + .desc = У заражённых активно слезятся глаза, из-за чего кажется, что они плачут. +ent-ActionDiseaseMuted = Немота + .desc = Мутация вызывает повреждение подъязычного нерва, приводя к параличу мышц языка, из-за чего больные теряют возможность нормально говорить. +ent-ActionDiseaseSlowness = Изнеможение + .desc = Вирус вызывает разрушение мышечных волокон, приводящее к атрофие и сопровождающееся слабостью. Снижает общую мобильность +ent-ActionDiseaseBleed = Кровопотеря + .desc = Вирус вызывает денатурацию гемоглобина крови, из-за чего у всех носителей появляется тяжелая степень анемии +ent-ActionDiseaseBlindness = Слепота + .desc = Длительная болезнь приводит к отмиранию зрительного нерва, что приводит к практически полной слепоте больного. +ent-ActionDiseaseInsult = Судороги + .desc = Длительная болезнь вызывает гиперстимуляцию двигательных нейронов, в результате чего больные могут испытывать перенапряжение мышц, приводящие к судорогам. diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/clothing/ears/headsets.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/clothing/ears/headsets.ftl index 3b9159eafd..013c5fc395 100644 --- a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/clothing/ears/headsets.ftl +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/clothing/ears/headsets.ftl @@ -4,3 +4,5 @@ ent-ClothingHeadsetMagistrate = гарнитура Магистрата .desc = Гарнитура магистрата, чтобы услышать последние слова зеков. ent-ClothingHeadsetLawyer = гарнитура Юриста .desc = Гарнитура юриста, что-бы защитить честь клоуна. +ent-ClothingHeadsetJEngineering = гарнитура тюремного инженерного отдела. + .desc = Гарнитура, которую используют тюремные инженеры. diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/humanoid.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/mobs/player/eviltwin.ftl similarity index 100% rename from Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/humanoid.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/entities/mobs/player/eviltwin.ftl diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/humanoid.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/mobs/player/fugitive.ftl similarity index 100% rename from Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/humanoid.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/entities/mobs/player/fugitive.ftl diff --git a/Resources/Locale/ru-RU/_sunrise/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/consumable/drinks/drinks_cans.ftl similarity index 97% rename from Resources/Locale/ru-RU/_sunrise/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/consumable/drinks/drinks_cans.ftl index f8956d397f..5ac6eb3e24 100644 --- a/Resources/Locale/ru-RU/_sunrise/prototypes/entities/objects/consumable/drinks/drinks_cans.ftl +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/consumable/drinks/drinks_cans.ftl @@ -1,4 +1,4 @@ ent-DrinkFourteenLokoPlusCan = банка Фоуртин Локо плюс .desc = Ремейк от Мэйсона Миллера популярного напитка Фоуртин Локо, но также как и в оригинале, пожалуйста пейте ответственно. ent-DrinkLukin = банка напитка Лукина - .desc = Не очень известный алкогольный напиток Лукина. Он обладает лечебными свойствами в деле телесных повреждений, но не стоит пить слишком много... \ No newline at end of file + .desc = Не очень известный алкогольный напиток Лукина. Он обладает лечебными свойствами в деле телесных повреждений, но не стоит пить слишком много... diff --git a/Resources/Locale/ru-RU/_sunrise/prototypes/entities/objects/consumable/food/snacks.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/consumable/food/snacks.ftl similarity index 64% rename from Resources/Locale/ru-RU/_sunrise/prototypes/entities/objects/consumable/food/snacks.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/consumable/food/snacks.ftl index 34f7b93940..e8d074d22b 100644 --- a/Resources/Locale/ru-RU/_sunrise/prototypes/entities/objects/consumable/food/snacks.ftl +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/consumable/food/snacks.ftl @@ -1,4 +1,6 @@ ent-FoodSnackMaysonEnergy = энергетический батончик Мэйсона .desc = Энергетический батончик состоящий из натуральных химикатов! ent-FoodSnackMaysonEnergyBar = энергетический батончик Мэйсона - .desc = Энергетический батончик состоящий из натуральных химикатов! \ No newline at end of file + .desc = Энергетический батончик состоящий из натуральных химикатов! +ent-FoodPacketMaysonEnergyTrash = обёртка энергетического батончика + .desc = { ent-FoodPacketTrash.desc } diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/misc/identification_cards.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/devices/identification_cards.ftl similarity index 100% rename from Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/misc/identification_cards.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/devices/identification_cards.ftl diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/sofia.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/borg_brains.ftl similarity index 52% rename from Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/sofia.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/borg_brains.ftl index 5bb6e8e5fa..0a53e0d23f 100644 --- a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/sofia.ftl +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/borg_brains.ftl @@ -1,2 +1,4 @@ +ent-Boris = роботический мозг + .desc = Искусственный мозг, способной к банальной интелектуальной активности. ent-Sofia = роботический мозг синдиката .desc = Искусственный мозг, способной к банальной интелектуальной активности. diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/boris.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/boris.ftl deleted file mode 100644 index 5ee99d68e0..0000000000 --- a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/objects/specific/boris.ftl +++ /dev/null @@ -1,2 +0,0 @@ -ent-Boris = роботический мозг - .desc = Искусственный мозг, способной к банальной интелектуальной активности. diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/pacificator/pacificator.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/structures/machines/pacificator.ftl similarity index 100% rename from Resources/Locale/ru-RU/_sunrise/_prototypes/pacificator/pacificator.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/entities/structures/machines/pacificator.ftl diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/structures/shuttles/thrusters.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/structures/shuttles/thrusters.ftl index bb7867ff84..186026c406 100644 --- a/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/structures/shuttles/thrusters.ftl +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/entities/structures/shuttles/thrusters.ftl @@ -1,2 +1,2 @@ ent-BigThruster = большой ракетный двигатель - .desc = { ent-Thruster } \ No newline at end of file + .desc = { ent-Thruster } diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/events.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/events.ftl deleted file mode 100644 index 466625f8f4..0000000000 --- a/Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/events.ftl +++ /dev/null @@ -1,2 +0,0 @@ -ent-EvilTwinSpawn = { ent-BaseGameRule } - .desc = { ent-BaseGameRule.desc } diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/roundstart.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/roundstart.ftl deleted file mode 100644 index dd72ce3176..0000000000 --- a/Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/roundstart.ftl +++ /dev/null @@ -1,2 +0,0 @@ -ent-EvilTwin = { ent-BaseGameRule } - .desc = { ent-BaseGameRule.desc } diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/events.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/events.ftl deleted file mode 100644 index b16e8af8bf..0000000000 --- a/Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/events.ftl +++ /dev/null @@ -1,2 +0,0 @@ -ent-FugitiveSpawn = { ent-BaseGameRule } - .desc = { ent-BaseGameRule.desc } diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/roundstart.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/roundstart.ftl deleted file mode 100644 index 0a381f28bc..0000000000 --- a/Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/roundstart.ftl +++ /dev/null @@ -1,2 +0,0 @@ -ent-Fugitive = { ent-BaseGameRule } - .desc = { ent-BaseGameRule.desc } diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/gamerules/events.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/gamerules/events.ftl new file mode 100644 index 0000000000..ec79eb797f --- /dev/null +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/gamerules/events.ftl @@ -0,0 +1,6 @@ +ent-DiseaseSpawn = { ent-BaseGameRule } + .desc = { ent-BaseGameRule.desc } +ent-FugitiveSpawn = { ent-BaseGameRule } + .desc = { ent-BaseGameRule.desc } +ent-EvilTwinSpawn = { ent-BaseGameRule } + .desc = { ent-BaseGameRule.desc } diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/gamerules/roundstart.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/gamerules/roundstart.ftl new file mode 100644 index 0000000000..265d642f5b --- /dev/null +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/gamerules/roundstart.ftl @@ -0,0 +1,6 @@ +ent-DiseaseRule = { ent-BaseGameRule } + .desc = { ent-BaseGameRule.desc } +ent-Fugitive = { ent-BaseGameRule } + .desc = { ent-BaseGameRule.desc } +ent-EvilTwin = { ent-BaseGameRule } + .desc = { ent-BaseGameRule.desc } diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/objective.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/objectives/eviltwin.ftl similarity index 100% rename from Resources/Locale/ru-RU/_sunrise/_prototypes/eviltwin/objective.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/objectives/eviltwin.ftl diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/objective.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/objectives/fugitive.ftl similarity index 100% rename from Resources/Locale/ru-RU/_sunrise/_prototypes/fugitive/objective.ftl rename to Resources/Locale/ru-RU/_sunrise/_prototypes/objectives/fugitive.ftl diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/roles/antags/disease.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/roles/antags/disease.ftl new file mode 100644 index 0000000000..3727c805b7 --- /dev/null +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/roles/antags/disease.ftl @@ -0,0 +1,2 @@ +ent-MobDisease = разумная болезнь + .desc = plague inc 2.0 diff --git a/Resources/Locale/ru-RU/_sunrise/_prototypes/statues/statues.ftl b/Resources/Locale/ru-RU/_sunrise/_prototypes/statues/statues.ftl new file mode 100644 index 0000000000..b229133f61 --- /dev/null +++ b/Resources/Locale/ru-RU/_sunrise/_prototypes/statues/statues.ftl @@ -0,0 +1,10 @@ +ent-StatueFurukai = статуя Фурукай + .desc = { ent-BaseStructure.desc } +ent-StatueDude = статуя Дюда + .desc = { ent-BaseStructure.desc } +ent-StatueFrank = статуя Фрэнка + .desc = { ent-BaseStructure.desc } +ent-StatueMooniverse = статуя Лунивёрса + .desc = { ent-BaseStructure.desc } +ent-StatueRib = статуя Риба + .desc = { ent-BaseStructure.desc } diff --git a/Resources/Locale/ru-RU/_sunrise/disease/disease.ftl b/Resources/Locale/ru-RU/_sunrise/disease/disease.ftl index 0e0df2af07..79dd4629fc 100644 --- a/Resources/Locale/ru-RU/_sunrise/disease/disease.ftl +++ b/Resources/Locale/ru-RU/_sunrise/disease/disease.ftl @@ -2,10 +2,6 @@ mob-name-disease = Разумная болезнь mob-description-disease = plague inc 2.0 ghostrole-disease-name = разумная болезнь ghostrole-disease-description = Обычная болезнь, которая вдруг решила стать разумной. С кем не бывает? -action-disease-infect-name = Заразить [10 ОЭ] -action-disease-infect-description = Попытка заразить существо [-10 очков эволюции]. Первые три попытки заражения не требуют очков эволюции. Выберите способность и нажмите ЛКМ по сущности, чтобы заразить. -action-disease-shop-name = Мутация -action-disease-shop-description = Открывает меню мутации disease-round-end-result = [color=green]На станции была разумная болезнь. Ей было:[/color] disease-round-end-result-infected = Заражено: [color=gold]{ $count }[/color] disease-round-end-result-infects = Заражённых в конце раунда: [color=gold]{ $count }[/color] diff --git a/Resources/Locale/ru-RU/_sunrise/disease/shop.ftl b/Resources/Locale/ru-RU/_sunrise/disease/shop.ftl index 46aaef2432..b418fc8a49 100644 --- a/Resources/Locale/ru-RU/_sunrise/disease/shop.ftl +++ b/Resources/Locale/ru-RU/_sunrise/disease/shop.ftl @@ -2,33 +2,5 @@ shop-disease-category-infect = Передача shop-disease-category-symptoms = Симптомы shop-disease-category-evolution = Улучшение shop-disease-currency = Очки эволюции -listing-disease-cough-name = Кашель -listing-disease-cough-description = Заражённые начинают кашлять. -listing-disease-sneeze-name = Чих -listing-disease-sneeze-description = Заражённые начинают чихать. -listing-disease-vomit-name = Тошнота -listing-disease-vomit-description = Заражённых начинает тоншить, вызывая рвоту. listing-disease-zombie-name = Зомбирование listing-disease-zombie-description = 70% текущих заражённых становятся зомби. Они так-же перестают быть заражёнными. -listing-disease-cry-name = Непроизвольные слёзы -listing-disease-cry-description = У заражённых активно слезятся глаза, из-за чего кажется, что они плачут. -listing-disease-base-chance-name = Улучшение заражения. [20 ОЭ] -listing-disease-base-chance-description = Действие 'заразить' имеет больший шанс на успех. [-20 очков эволюции] -listing-disease-infect-chance-name = Повышение заразности. [15 ОЭ] -listing-disease-infect-chance-description = Увеличивает заразность вируса. Влияет на шанс заражения. [-15 очков эволюции] -listing-disease-shield-name = Повышение устойчивости. [15 ОЭ] -listing-disease-shield-description = Повышает устойчивость болезни к лекарствам. Влияет на скорость лечения при употреблении лекарства. [-15 очков эволюции] -listing-disease-lethal-name = Повышение летальности. [15 ОЭ] -listing-disease-lethal-description = Увеличивает получаемый урон от симптомов. Влияет на получаемый при кашле урон. [-15 очков эволюции] -listing-disease-narcolepsy-name = Сонливость -listing-disease-narcolepsy-description = У заражённых появляется постоянное желание спать, с которым они иногда не могут справиться. -listing-disease-muted-name = Немота -listing-disease-muted-description = Мутация вызывает повреждение подъязычного нерва, приводя к параличу мышц языка, из-за чего больные теряют возможность нормально говорить. -listing-disease-slowness-name = Изнеможение -listing-disease-slowness-description = Вирус вызывает разрушение мышечных волокон, приводящее к атрофие и сопровождающееся слабостью. Снижает общую мобильность -listing-disease-bleed-name = Кровопотеря -listing-disease-bleed-description = Вирус вызывает денатурацию гемоглобина крови, из-за чего у всех носителей появляется тяжелая степень анемии -listing-disease-blindness-name = Слепота -listing-disease-blindness-description = Длительная болезнь приводит к отмиранию зрительного нерва, что приводит к практически полной слепоте больного. -listing-disease-insult-name = Судороги -listing-disease-insult-description = Длительная болезнь вызывает гиперстимуляцию двигательных нейронов, в результате чего больные могут испытывать перенапряжение мышц, приводящие к судорогам. diff --git a/Resources/Locale/ru-RU/_sunrise/prototypes/entities/clothing/ears/headsets.ftl b/Resources/Locale/ru-RU/_sunrise/prototypes/entities/clothing/ears/headsets.ftl deleted file mode 100644 index ed301765ed..0000000000 --- a/Resources/Locale/ru-RU/_sunrise/prototypes/entities/clothing/ears/headsets.ftl +++ /dev/null @@ -1,2 +0,0 @@ -ent-ClothingHeadsetJEngineering = гарнитура тюремного инженерного отдела. - .desc = Гарнитура, которую используют тюремные инженеры. diff --git a/Resources/Locale/ru-RU/_sunrise/reagents/meta/consumable/drink/soda.ftl b/Resources/Locale/ru-RU/_sunrise/reagents/meta/consumable/drink/soda.ftl index 8b3ecb9bfb..58fe3fa750 100644 --- a/Resources/Locale/ru-RU/_sunrise/reagents/meta/consumable/drink/soda.ftl +++ b/Resources/Locale/ru-RU/_sunrise/reagents/meta/consumable/drink/soda.ftl @@ -1,2 +1,2 @@ reagent-name-fourteen-loko-plus = фоуртин локо плюс -reagent-desc-fourteen-loko-plus = Сильно переработанная жидкая субстанция с элиментами стимулирующих веществ, едва ли соответствующая межгалактическим стандартам безопасности для безалкогольного напитка. \ No newline at end of file +reagent-desc-fourteen-loko-plus = Сильно переработанная жидкая субстанция с элиментами стимулирующих веществ, едва ли соответствующая межгалактическим стандартам безопасности для безалкогольного напитка. diff --git a/Resources/Locale/ru-RU/accent/german.ftl b/Resources/Locale/ru-RU/accent/german.ftl new file mode 100644 index 0000000000..3926b0b2b6 --- /dev/null +++ b/Resources/Locale/ru-RU/accent/german.ftl @@ -0,0 +1,134 @@ +accent-german-words-1 = да +accent-german-words-replace-1 = ja +accent-german-words-2 = нет +accent-german-words-replace-2 = nein +accent-german-words-3 = тот +accent-german-words-replace-3 = Das +accent-german-words-4 = дерьмо +accent-german-words-replace-4 = scheisse +accent-german-words-5 = сосиска +accent-german-words-replace-5 = wurst +accent-german-words-6 = сосиски +accent-german-words-replace-6 = würste +accent-german-words-7 = мужик +accent-german-words-replace-7 = mann +accent-german-words-8 = мужики +accent-german-words-replace-8 = männer +accent-german-words-9 = женщина +accent-german-words-9-2 = леди +accent-german-words-replace-9 = frau +accent-german-words-10 = женщины +accent-german-words-10-2 = дамы +accent-german-words-replace-10 = frauen +accent-german-words-11 = джентельмен +accent-german-words-replace-11 = herr +accent-german-words-12 = джентельмены +accent-german-words-replace-12 = herren +accent-german-words-13 = мой бог +accent-german-words-replace-13 = mein gott +accent-german-words-14 = мой +accent-german-words-replace-14 = mein +accent-german-words-15 = здесь +accent-german-words-replace-15 = hier +accent-german-words-16 = идиот +accent-german-words-replace-16 = dummkopf +accent-german-words-17 = идиоты +accent-german-words-replace-17 = dummköpfe +accent-german-words-18 = бабочка +accent-german-words-replace-18 = schmetterling +accent-german-words-19 = машина +accent-german-words-replace-19 = maschine +accent-german-words-replace-19-2 = auto +accent-german-words-20 = машины +accent-german-words-replace-20 = maschinen +accent-german-words-21 = осторожно +accent-german-words-replace-21 = achtung +accent-german-words-22 = музыка +accent-german-words-replace-22 = musik +accent-german-words-23 = капитан +accent-german-words-replace-23 = kapitän +accent-german-words-24 = кебаб +accent-german-words-replace-24 = döner +accent-german-words-25 = мышь +accent-german-words-replace-25 = maus +accent-german-words-26 = что +accent-german-words-replace-26 = wat +accent-german-words-27 = спасибо вам +accent-german-words-replace-27 = dankeschön +accent-german-words-28 = спасибо +accent-german-words-replace-28 = danke +accent-german-words-29 = будьте здоровы +accent-german-words-replace-29 = gesundheit +accent-german-words-30 = огнемёт +accent-german-words-replace-30 = flammenwerfer +accent-german-words-31 = призрак +accent-german-words-replace-31 = poltergeist +accent-german-words-32 = сорняк +accent-german-words-32-2 = cabbage +accent-german-words-replace-32 = kraut +accent-german-words-33 = вотка +accent-german-words-replace-33 = wodka +accent-german-words-34 = рюкзак +accent-german-words-replace-34 = rucksack +accent-german-words-35 = медицина +accent-german-words-replace-35 = medizin +accent-german-words-36 = акцент +accent-german-words-replace-36 = akzent +accent-german-words-37 = аномалия +accent-german-words-replace-37 = anomalie +accent-german-words-38 = артефакт +accent-german-words-38-2 = artefact +accent-german-words-replace-38 = artefakt +accent-german-words-39 = тупой +accent-german-words-replace-39 = dumm +accent-german-words-40 = глупый +accent-german-words-replace-40 = doof +accent-german-words-41 = замечательный +accent-german-words-41-2 = прекрастный +accent-german-words-replace-41 = wunderbar +accent-german-words-42 = опастно +accent-german-words-replace-42 = warnung +accent-german-words-43 = опастности +accent-german-words-replace-43 = warnungen +accent-german-words-44 = и +accent-german-words-replace-44 = und +accent-german-words-45 = карп +accent-german-words-replace-45 = karpfen +accent-german-words-46 = командир +accent-german-words-replace-46 = kommandant +accent-german-words-47 = пиво +accent-german-words-replace-47 = bier +accent-german-words-48 = привет +accent-german-words-replace-48 = hallo +accent-german-words-49 = здравия +accent-german-words-replace-49 = guten Tag +accent-german-words-50 = помощь +accent-german-words-replace-50 = hilfe +accent-german-words-51 = до свидания +accent-german-words-replace-51 = auf Wiedersehen +accent-german-words-52 = пока +accent-german-words-replace-52 = tschüss +accent-german-words-53 = пока пока +accent-german-words-53-2 = пока-пока +accent-german-words-replace-53 = tschau +accent-german-words-54 = фантастически +accent-german-words-replace-54 = fantastisch +accent-german-words-55 = генокрад +accent-german-words-replace-55 = doppelgänger +accent-german-words-56 = запрещенный +accent-german-words-replace-56 = verboten +accent-german-words-57 = быстрый +accent-german-words-57-2 = быстро +accent-german-words-replace-57 = schnell +accent-german-words-58 = мед +accent-german-words-replace-58 = krankenhaus +accent-german-words-59 = катушка теслы +accent-german-words-replace-59 = tesla coil +accent-german-words-60 = катушки теслы +accent-german-words-replace-60 = tesla coils +accent-german-words-61 = teslaloose +accent-german-words-61-2 = tesloose +accent-german-words-61-3 = lightning ball +accent-german-words-61-4 = шаровая молния +accent-german-words-61-5 = тесла +accent-german-words-replace-61 = kugelblitz diff --git a/Resources/Locale/ru-RU/administration/ui/player-panel.ftl b/Resources/Locale/ru-RU/administration/ui/player-panel.ftl index f9095c7600..93ab7c2ea9 100644 --- a/Resources/Locale/ru-RU/administration/ui/player-panel.ftl +++ b/Resources/Locale/ru-RU/administration/ui/player-panel.ftl @@ -5,7 +5,7 @@ player-panel-bans = Всего банов: { $totalBans } player-panel-rolebans = Всего роль банов: { $totalRoleBans } player-panel-notes = Всего заметок: { $totalNotes } player-panel-playtime = Всего наиграно времени: { $days }д:{ $hours }ч:{ $minutes }м -player-panel-shared-connections = Shared Connections: { $sharedConnections } +player-panel-shared-connections = Общие подключения: { $sharedConnections } player-panel-copy-username = Копировать player-panel-show-notes = Заметки player-panel-show-bans = Показать баны @@ -18,4 +18,5 @@ player-panel-ban = Бан player-panel-logs = Логи player-panel-delete = Удалить player-panel-rejuvenate = Воскресить -player-panel-false = False +player-panel-false = Нет +player-panel-true = Да diff --git a/Resources/Locale/ru-RU/atmos/gas-analyzer-component.ftl b/Resources/Locale/ru-RU/atmos/gas-analyzer-component.ftl index 56b89b9c11..aa7b6e6b7f 100644 --- a/Resources/Locale/ru-RU/atmos/gas-analyzer-component.ftl +++ b/Resources/Locale/ru-RU/atmos/gas-analyzer-component.ftl @@ -1,5 +1,6 @@ ## Entity +gas-analyzer-object-out-of-range = Объект вышел за пределы зоны обнаружения. gas-analyzer-component-player-cannot-reach-message = Вы не можете туда достать. gas-analyzer-shutoff = Газоанализатор выключается. diff --git a/Resources/Locale/ru-RU/botany/seeds.ftl b/Resources/Locale/ru-RU/botany/seeds.ftl new file mode 100644 index 0000000000..1f1b84a0c8 --- /dev/null +++ b/Resources/Locale/ru-RU/botany/seeds.ftl @@ -0,0 +1,2 @@ +botany-plant-seedsrestored = Слышен едва уловимый звук шуршания листьев. +botany-plant-seedsdestroyed = Семена на растении начинают трещать и падать! diff --git a/Resources/Locale/ru-RU/cargo/bounties.ftl b/Resources/Locale/ru-RU/cargo/bounties.ftl index 86c1c6ecfb..a6472f767b 100644 --- a/Resources/Locale/ru-RU/cargo/bounties.ftl +++ b/Resources/Locale/ru-RU/cargo/bounties.ftl @@ -128,6 +128,7 @@ bounty-description-torch = Шеф-повар оживил всех обезья bounty-description-medkit-box = Центком ставит спектакль в больнице, и нуждается в реквизите. Просто пришлите нам несколько пустых аптечек, и спектакль состоится! bounty-description-cardobard-box = Завтра состоится премьера новой пьесы "Пришествие картонборга", а костюмеры совершенно не готовы. Пришлите нам несколько коробок для оформления. bounty-description-wine = Новая библиотекарша и квартирмейстер влюбились друг в друга по уши после того, как она застала его за разборкой книжных полок на дрова. Пришлите пару бутылок вина (или банок, если хотите), чтобы свидание прошло удачно. +bounty-description-cardboard-box = "The Cardborgs Cometh" is a new play premiering tomorrow, and the costuming team is woefully unprepared. Send us some boxes to work with. bounty-description-cotton-boll = Огромный рой таракамолей съел всю бумагу и ткань на станции. Пришлите нам немного хлопка, чтобы наши крылатые члены экипажа остались сыты. bounty-description-microwave-machine-board = Мистер Хихстер решил, что будет забавно позасовывать металлические вилки во все кухонные микроволновки. Помогите нам отремонтировать их, пока повара не принялись готовить клоунбургеры. bounty-description-lasergun = Караван утилизаторов запрашивает большую партию лазерного оружия для уничтожения улья ксеноморфов. diff --git a/Resources/Locale/ru-RU/clothing/components/self-unremovable-clothing-component.ftl b/Resources/Locale/ru-RU/clothing/components/self-unremovable-clothing-component.ftl new file mode 100644 index 0000000000..a0a7aa1dc7 --- /dev/null +++ b/Resources/Locale/ru-RU/clothing/components/self-unremovable-clothing-component.ftl @@ -0,0 +1 @@ +comp-self-unremovable-clothing = Это нельзя снять без посторонней помощи. diff --git a/Resources/Locale/ru-RU/datasets/figurines.ftl b/Resources/Locale/ru-RU/datasets/figurines.ftl index 6099facbf3..068e531079 100644 --- a/Resources/Locale/ru-RU/datasets/figurines.ftl +++ b/Resources/Locale/ru-RU/datasets/figurines.ftl @@ -25,12 +25,18 @@ figurines-musician-2 = Never gonna let you down! figurines-captain-1 = Экипаж, диск от ядерной бомбы надежно спрятан у меня в заднице. figurines-captain-2 = Слава НТ! +figurines-captain-3 = Ядерный диск в безопасности. Где? Где-то. figurines-hos-1 = Космический закон? Что? figurines-hos-2 = Пристрелил клоуна. +figurines-hos-3 = Да, я выстрелил в клоуна. Нет, я не жалею об этом. figurines-warden-1 = Казните его за взлом! +figurines-warden-2 = Навсегда накажем этого урода за оскорбление! +figurines-warden-3 = Мы абсолютно честно обращаемся со всеми и НЕ злоупотребляем нашими заключенными. figurines-detective-1 = Это сделал дворецкий. +figurines-detective-2 = Мне нужно немного виски после этого. figurines-security-1 = Я и есть закон! figurines-security-2 = Вы нарушили статью 1984. +figurines-security-3 = Когда мне становится скучно, я использую клоуна для стрельбы. figurines-lawyer-1 = Лучше позвоните Солу! figurines-lawyer-2 = Протестую! figurines-cargotech-1 = ДРАКОН НА АТЗ! diff --git a/Resources/Locale/ru-RU/escape-menu/ui/options-menu.ftl b/Resources/Locale/ru-RU/escape-menu/ui/options-menu.ftl index 80607fd82c..2733d0642f 100644 --- a/Resources/Locale/ru-RU/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/ru-RU/escape-menu/ui/options-menu.ftl @@ -74,6 +74,8 @@ ui-options-scale-175 = 175% ui-options-scale-200 = 200% ui-options-hud-theme = Тема HUD: ui-options-hud-theme-default = По умолчанию +ui-options-hud-layout-default = Обычный +ui-options-hud-layout-separated = Раздельный ui-options-hud-theme-plasmafire = Плазма ui-options-hud-theme-slimecore = Слаймкор ui-options-hud-theme-clockwork = Механизм @@ -149,6 +151,9 @@ ui-options-static-storage-ui = Закрепить интерфейс храни ui-options-function-smart-equip-backpack = Умная экипировка в рюкзак ui-options-function-smart-equip-belt = Умная экипировка на пояс ui-options-function-open-backpack = Открыть рюкзак +ui-options-function-rotate-object-clockwise = Вращать по часовой стрелке +ui-options-function-rotate-object-counterclockwise = Вращать против часовой стрелки +ui-options-function-flip-object = Повернуть ui-options-function-open-belt = Открыть пояс ui-options-function-throw-item-in-hand = Бросить предмет ui-options-function-try-pull-object = Тянуть объект diff --git a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-allatonce.ftl b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-allatonce.ftl index 4984b07749..c43e0f4e5c 100644 --- a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-allatonce.ftl +++ b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-allatonce.ftl @@ -1,2 +1,4 @@ all-at-once-title = Всё и сразу all-at-once-description = Это просто не ваш день... +aller-at-once-title = Всё и вся сразу +aller-at-once-description = Вы теперь серьёзно накосячили. Теперь вы *действительно* накосячили. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-survival.ftl b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-survival.ftl index 3400f62482..1164cc978d 100644 --- a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-survival.ftl +++ b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-survival.ftl @@ -1,2 +1,4 @@ survival-title = Выживание survival-description = Внутренние угрозы отсутствуют, но как долго станция сможет продержаться в обстановке всё более разрушительных и частых событий? +kessler-syndrome-title = Синдром Кесслера +kessler-syndrome-description = Нет внутренних угроз, но станция быстро попадает в пояс метеоров! \ No newline at end of file diff --git a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-zombies.ftl b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-zombies.ftl index 51cdb2063b..4d98681e47 100644 --- a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-zombies.ftl +++ b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-zombies.ftl @@ -1,5 +1,7 @@ zombie-title = Зомби zombie-description = На станции появились ожившие мертвецы! Работайте сообща с другими членами экипажа, чтобы пережить эпидемию и защитить станцию. +zombieteors-title = Зомбитеоры +zombieteors-description = Нежить была выпущена на станцию на фоне катастрофического метеоритного дождя! Работайте вместе с вашим экипажем и делайте все возможное, чтобы выжить! zombie-not-enough-ready-players = Недостаточно игроков готовы к игре! { $readyPlayersCount } игроков из необходимых { $minimumPlayers } готовы. Нельзя запустить пресет Зомби. zombie-no-one-ready = Нет готовых игроков! Нельзя запустить пресет Зомби. zombie-patientzero-role-greeting = Вы — нулевой пациент. Снарядитесь и подготовьтесь к своему превращению. Ваша цель - захватить станцию, заразив при этом как можно больше членов экипажа. diff --git a/Resources/Locale/ru-RU/guidebook/chemistry/effects.ftl b/Resources/Locale/ru-RU/guidebook/chemistry/effects.ftl index 8b2784c568..caabdce293 100644 --- a/Resources/Locale/ru-RU/guidebook/chemistry/effects.ftl +++ b/Resources/Locale/ru-RU/guidebook/chemistry/effects.ftl @@ -351,3 +351,13 @@ reagent-effect-guidebook-plant-robust-harvest = [1] Повышает *[other] повышают } потенцию растения путём { $increase } до максимума в { $limit }. Приводит к тому, что растение теряет свои семена, когда потенция достигает { $seedlesstreshold }. Попытка повысить потенцию свыше { $limit } может вызвать снижение урожайности с вероятностью 10%. +reagent-effect-guidebook-plant-seeds-add = + { $chance -> + [1] Restores the + *[other] restore the + } seeds of the plant +reagent-effect-guidebook-plant-seeds-remove = + { $chance -> + [1] Removes the + *[other] remove the + } seeds of the plant diff --git a/Resources/Locale/ru-RU/guidebook/chemistry/plant-attributes.ftl b/Resources/Locale/ru-RU/guidebook/chemistry/plant-attributes.ftl index 1df2cde142..38e6f8668e 100644 --- a/Resources/Locale/ru-RU/guidebook/chemistry/plant-attributes.ftl +++ b/Resources/Locale/ru-RU/guidebook/chemistry/plant-attributes.ftl @@ -1,5 +1,6 @@ plant-attribute-growth = возраст plant-attribute-water = уровень воды +plant-attribute-potency = потенция plant-attribute-weeds = уровень сорняков plant-attribute-toxins = уровень токсинов plant-attribute-nutrition = уровень питательных веществ diff --git a/Resources/Locale/ru-RU/nutrition/components/food-sequence.ftl b/Resources/Locale/ru-RU/nutrition/components/food-sequence.ftl index a1f1992837..3bbae734f3 100644 --- a/Resources/Locale/ru-RU/nutrition/components/food-sequence.ftl +++ b/Resources/Locale/ru-RU/nutrition/components/food-sequence.ftl @@ -1,7 +1,79 @@ food-sequence-no-space = Вы не можете положить больше! food-sequence-standart-gen = { $prefix }{ $content }{ $suffix } +food-sequence-content-chicken = курица +food-sequence-content-duck = утка +food-sequence-content-crab = крабы +food-sequence-content-dragon = дракон +food-sequence-content-snake = змея +food-sequence-content-xeno = ксеноморф +food-sequence-content-rouny = роуни +food-sequence-content-tomato = помидор +food-sequence-content-salami = салями +food-sequence-content-slime = слизь +food-sequence-content-clown = клоун +food-sequence-content-pea = горох +food-sequence-content-bungo = бунго +food-sequence-content-banana = банан +food-sequence-content-mimana = мимана +food-sequence-content-lemon = лимон +food-sequence-content-lemoon = лему́н +food-sequence-content-lime = лайм +food-sequence-content-orange = апельсин +food-sequence-content-potato = картофель +food-sequence-content-apple = яблоко +food-sequence-content-cocoa = какао +food-sequence-content-corn = кукуруза +food-sequence-content-chili = чили +food-sequence-content-chilly = охлаждённый +food-sequence-content-mushroom = грибы +food-sequence-content-aloe = алоэ +food-sequence-content-poppy = мак +food-sequence-content-lily = лилия +food-sequence-content-soy = соя +food-sequence-content-cheese = сыр +food-sequence-content-chevre = chèvre +food-sequence-content-tofu = тофу +food-sequence-content-brain = мозг +food-sequence-content-tongue = язык +food-sequence-content-ears = ухо +food-sequence-content-stomach = желудок +food-sequence-content-liver = печень +food-sequence-content-clump = ком +food-sequence-content-raw-meat = сырое мясо +food-sequence-content-meat = мясо +food-sequence-content-carp = карп +food-sequence-content-bear = медвежатина +food-sequence-content-penguin = мясо пингвина +food-sequence-content-corgi = мясо корги +food-sequence-content-goliath = мясо голиафа +food-sequence-content-rat = крыса +food-sequence-content-lizard = мясо ящерицы +food-sequence-content-plant = растительное мясо +food-sequence-content-rotten = гнилое мясо +food-sequence-content-spider = паучьи лапки +food-sequence-content-carrot = морковь +food-sequence-content-cabbage = капуста +food-sequence-content-garlic = чеснок +food-sequence-content-pineapple = ананас +food-sequence-content-onion = лук +food-sequence-content-ambrosia = амброзия +food-sequence-content-galaxy = галактический чертополох +food-sequence-content-glasstle = гластл +food-sequence-content-gatfruit = гатфрут +food-sequence-content-koibean = койбина +food-sequence-content-watermelon = арбуз +food-sequence-content-cannabis = каннабис +food-sequence-content-rainbow-cannabis = радужный каннабис +food-sequence-content-tobacco = табак +food-sequence-content-hamster = хомяк +food-sequence-content-suppermatter = супермасса +food-sequence-content-capfruit = капфрут +food-sequence-content-berries = ягоды +food-sequence-content-spacemans-trumpet = космический духовой инструмент #Burger +food-sequence-burger-gen = { $content }бургер food-sequence-burger-suffix = бургер +food-sequence-burger-content-meat = мясно food-sequence-burger-content-raw-meat = сырой food-sequence-burger-content-carp = карпо food-sequence-burger-content-bear = медведь @@ -21,6 +93,7 @@ food-sequence-burger-content-clump = комок food-sequence-burger-content-snake = змея food-sequence-burger-content-xeno = ксено food-sequence-burger-content-tomato = томат +food-sequence-burger-content-capfruit = кап food-sequence-burger-content-salami = салями food-sequence-burger-content-clown = клоун food-sequence-burger-content-slime = слайм @@ -28,8 +101,14 @@ food-sequence-burger-content-pea = пеа food-sequence-burger-content-banana = банан food-sequence-burger-content-mimana = мимана food-sequence-burger-content-carrot = морковно +food-sequence-burger-content-suppermatter = суппер +food-sequence-burger-content-hamster = хомя +food-sequence-burger-content-berries = ягодно +food-sequence-burger-content-spacemans-trumpet = космострубко food-sequence-burger-content-cabbage = капуста +food-sequence-taco-gen = тако с { $content } food-sequence-burger-content-garlic = чесночно +food-sequence-skewer-gen = { $content } кебаб food-sequence-burger-content-lemon = лемон food-sequence-burger-content-orange = апельсино food-sequence-burger-content-pineapple = ананасо diff --git a/Resources/Locale/ru-RU/power/components/radiation-collector.ftl b/Resources/Locale/ru-RU/power/components/radiation-collector.ftl index 437be1072a..349fe6a367 100644 --- a/Resources/Locale/ru-RU/power/components/radiation-collector.ftl +++ b/Resources/Locale/ru-RU/power/components/radiation-collector.ftl @@ -1,11 +1,13 @@ power-radiation-collector-gas-tank-missing = Выемка для баллона с плазмой [color=darkred]пустует[/color]. -power-radiation-collector-gas-tank-present = Выемка для баллона с плазмой [color=darkgreen]заполнена[/color] и индикатор баллона находится на отметке [color={$fullness -> - *[0]red]пусто - [1]red]мало - [2]yellow]заполнено наполовину - [3]lime]заполнено -}[/color]. -power-radiation-collector-enabled = Находится в режиме [color={$state -> - [true] darkgreen]вкл - *[false] darkred]выкл -}[/color]. +power-radiation-collector-gas-tank-present = + Выемка для баллона с плазмой [color=darkgreen]заполнена[/color] и индикатор баллона находится на отметке [color={ $fullness -> + *[0] red]пусто + [1] red]мало + [2] yellow]заполнено наполовину + [3] lime]заполнено + }[/color]. +power-radiation-collector-enabled = + Находится в режиме [color={ $state -> + [true] darkgreen]вкл + *[false] darkred]выкл + }[/color]. diff --git a/Resources/Locale/ru-RU/preferences/loadout-groups.ftl b/Resources/Locale/ru-RU/preferences/loadout-groups.ftl index 364f48e6f1..790f2b0306 100644 --- a/Resources/Locale/ru-RU/preferences/loadout-groups.ftl +++ b/Resources/Locale/ru-RU/preferences/loadout-groups.ftl @@ -31,6 +31,7 @@ loadout-group-passenger-mask = Пассажир, маска loadout-group-passenger-gloves = Пассажир, перчатки loadout-group-passenger-outerclothing = Пассажир, верхняя одежда loadout-group-passenger-shoes = Пассажир, обувь +loadout-group-passenger-neck = Пассажир, шея loadout-group-bartender-head = Бармен, голова loadout-group-bartender-jumpsuit = Бармен, комбинезон loadout-group-bartender-outerclothing = Бармен, верхняя одежда @@ -133,6 +134,7 @@ loadout-group-detective-neck = Детектив, шея loadout-group-detective-jumpsuit = Детектив, комбинезон loadout-group-detective-outerclothing = Детектив, верхняя одежда loadout-group-security-cadet-jumpsuit = Кадет СБ, комбинезон +loadout-group-security-star = Звезда СБ # Medical loadout-group-medical-gloves = Медицинский отдел, перчатки loadout-group-medical-mask = Медицинский отдел, маска diff --git a/Resources/Locale/ru-RU/preferences/ui/markings-picker.ftl b/Resources/Locale/ru-RU/preferences/ui/markings-picker.ftl index b8ebe44098..02bd268433 100644 --- a/Resources/Locale/ru-RU/preferences/ui/markings-picker.ftl +++ b/Resources/Locale/ru-RU/preferences/ui/markings-picker.ftl @@ -10,6 +10,7 @@ marking-used = { $marking-name } marking-used-forced = { $marking-name } (Принудительно) marking-slot-add = Добавить marking-slot-remove = Удалить +marking-slot = Слот { $number } # Categories diff --git a/Resources/Locale/ru-RU/reagents/meta/botany.ftl b/Resources/Locale/ru-RU/reagents/meta/botany.ftl index 43d880bfcc..c382ce8c6f 100644 --- a/Resources/Locale/ru-RU/reagents/meta/botany.ftl +++ b/Resources/Locale/ru-RU/reagents/meta/botany.ftl @@ -14,3 +14,5 @@ reagent-name-ammonia = аммиак reagent-desc-ammonia = Эффективное удобрение, дающее растениям питательные вещества. reagent-name-diethylamine = диэтиламин reagent-desc-diethylamine = Очень мощное удобрение, насыщает растения питательными веществами, уничтожает вредителей, а иногда даже ускоряет рост. +reagent-name-sedin = седин +reagent-desc-sedin = Модифицированная версия диэтилового аммиака, которая может восстанавливать семена на растениях за счет силы действия. diff --git a/Resources/Locale/ru-RU/research/technologies.ftl b/Resources/Locale/ru-RU/research/technologies.ftl index 5c09f0b0dc..d102cc6743 100644 --- a/Resources/Locale/ru-RU/research/technologies.ftl +++ b/Resources/Locale/ru-RU/research/technologies.ftl @@ -24,6 +24,7 @@ research-technology-biochemical-stasis = Биохимический стазис research-technology-mechanized-treatment = Механизированная подготовка research-technology-bluespace-chemistry = Блюспейс-химия research-technology-salvage-weapons = Утилизаторское оружие +research-technology-special-means = Специальные средства research-technology-draconic-munitions = Драконьи боеприпасы research-technology-nonlethal-ammunition = Не летальная аммуниция research-technology-uranium-munitions = Урановые боеприпасы diff --git a/Resources/Locale/ru-RU/salvage/salvage-magnet.ftl b/Resources/Locale/ru-RU/salvage/salvage-magnet.ftl index c141c684e8..17a895e953 100644 --- a/Resources/Locale/ru-RU/salvage/salvage-magnet.ftl +++ b/Resources/Locale/ru-RU/salvage/salvage-magnet.ftl @@ -32,6 +32,12 @@ dungeon-config-proto-ClusterAsteroid = Астероидный кластер dungeon-config-proto-SpindlyAsteroid = Астероидная спираль dungeon-config-proto-SwissCheeseAsteroid = Фрагменты астероидов # Wrecks +salvage-map-wreck = Спасательный обломок +salvage-map-wreck-desc-size = Размер: +salvage-map-wreck-size-small = [color=lime]Маленький[/color] +salvage-map-wreck-size-medium = [color=cornflowerblue]Средний[/color] +salvage-map-wreck-size-large = [color=orchid]Большой[/color] +# Wrecks salvage-map-proto-Small1 = Инженерное хранилище salvage-map-proto-Small2 = Игровой уголок salvage-map-proto-Small3 = Прачечная diff --git a/Resources/Locale/ru-RU/seeds/seeds.ftl b/Resources/Locale/ru-RU/seeds/seeds.ftl index c363fe194c..eaf4b480e7 100644 --- a/Resources/Locale/ru-RU/seeds/seeds.ftl +++ b/Resources/Locale/ru-RU/seeds/seeds.ftl @@ -122,3 +122,5 @@ seeds-cotton-name = хлопок seeds-cotton-display-name = куст хлопка seeds-pyrotton-name = пирохлопок seeds-pyrotton-display-name = куст пирохлопка +seeds-capfruit-name = капфрут +seeds-capfruit-display-name = дерево капфрута \ No newline at end of file diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/food_drinks/food_ingredients.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/food_drinks/food_ingredients.ftl new file mode 100644 index 0000000000..38b459ace2 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/food_drinks/food_ingredients.ftl @@ -0,0 +1,3 @@ +ent-RandomIngredient = спавнер случайных ингредиентов + .suffix = Не растительные + .desc = { ent-MarkerBase.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/food_drinks/food_meat.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/food_drinks/food_meat.ftl new file mode 100644 index 0000000000..0674c0e33c --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/food_drinks/food_meat.ftl @@ -0,0 +1,2 @@ +ent-RandomMeat = спавнер случайного мяса + .desc = { ent-MarkerBase.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/salvage.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/salvage.ftl index 09a8f85db3..faf334134a 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/salvage.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/salvage.ftl @@ -1,7 +1,5 @@ ent-SalvageMaterialCrateSpawner = спавнер обломок ящик материалов .desc = { ent-MarkerBase.desc } -ent-SalvageCanisterSpawner = спавнер обломок канистра - .desc = { ent-MarkerBase.desc } ent-SalvageLootSpawner = спавнер обломок добыча .desc = { ent-MarkerBase.desc } ent-SalvageMobSpawner = спавнер обломок моб diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/salvage/spawners.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/salvage/spawners.ftl new file mode 100644 index 0000000000..ce11c1bef0 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/markers/spawners/random/salvage/spawners.ftl @@ -0,0 +1,37 @@ +ent-SalvageSpawnerScrapCommon = Спавнер металлолома + .suffix = Обычный, 50% + .desc = { ent-MarkerBase.desc } +ent-SalvageSpawnerScrapCommon75 = { ent-SalvageSpawnerScrapCommon } + .suffix = Обычный, 75% + .desc = { ent-SalvageSpawnerScrapCommon.desc } +ent-SalvageSpawnerScrapValuable = Спавнер металлолома + .suffix = Ценный, 50% + .desc = { ent-MarkerBase.desc } +ent-SalvageSpawnerScrapValuable75 = { ent-SalvageSpawnerScrapValuable } + .suffix = Ценный, 75% + .desc = { ent-SalvageSpawnerScrapValuable.desc } +ent-SalvageSpawnerTreasure = Спавнер сокровищ + .suffix = Обычный, 75% + .desc = { ent-MarkerBase.desc } +ent-SalvageSpawnerTreasureValuable = Спавнер сокровищ + .suffix = Ценный, 75% + .desc = { ent-MarkerBase.desc } +ent-SalvageSpawnerEquipment = Спавнер снаряжения + .suffix = Обычный, 75% + .desc = { ent-MarkerBase.desc } +ent-SalvageSpawnerEquipmentValuable = Спавнер снаряжения + .suffix = Ценный, 75% + .desc = { ent-MarkerBase.desc } +ent-SalvageCanisterSpawner = спавнер обломок канистра + .desc = { ent-MarkerBase.desc } +ent-SalvageSpawnerMobMagnet = Спавнер мобов-магнитов + .suffix = 50% + .desc = { ent-MarkerBase.desc } +ent-SalvageSpawnerMobMagnet75 = { ent-SalvageSpawnerMobMagnet } + .suffix = 75% + .desc = { ent-SalvageSpawnerMobMagnet.desc } +ent-SalvageSpawnerMobMagnet100 = { ent-SalvageSpawnerMobMagnet } + .suffix = 100% + .desc = { ent-SalvageSpawnerMobMagnet.desc } +ent-SalvageSpawnerMobShark = Спавнер акуломиннов + .desc = { ent-MarkerBase.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/treasure.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/treasure.ftl new file mode 100644 index 0000000000..d6f6f71985 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/treasure.ftl @@ -0,0 +1,22 @@ +ent-TreasureDatadiskEncrypted = зашифрованный дата-диск + .desc = Старый дата-диск. Кто-то более сведущий, чем вы, мог бы его расшифровать и узнать что-то интересное. Всё, что вы можете сделать — это продать его. +ent-TreasureHardDiskDrive = жёсткий диск + .desc = Дисковый накопитель, предназначенный для старых земных технологий, примерно 1980 года. Возможно, найдётся какой-то коллекционер, который захочет заполучить это. +ent-TreasureFlopDiskDrive = дисковод для гибких дисков + .desc = Дисковод для чтения информации с гибких дисков. Жаль, что в наши дни на них хранятся только операции ядерных бомб. +ent-TreasureCDDrive = CD-дисковод + .desc = Устройство для чтения данных с компакт-дисков. В наше время это не самое полезное устройство, если вы, конечно, не собираетесь куда-то лететь. +ent-TreasureCPUSupercharged = сверхзаряженный процессор + .desc = Какая-то супер-инопланетная космотехника. Жаль, что все компьютеры уже оснащены процессорами. +ent-TreasureSampleTube = пробирка с образцом + .desc = Стеклянная пробирка с какой-то субстанцией. Выглядит хрупкой. +ent-TreasureCoinIron = монета + .desc = Плоский кусок металла. Если бы она всё ещё была в обращении, инфляция сделала бы её бесполезной. Но так как это не так, она стоит достаточно, чтобы хранить её в книге. +ent-TreasureCoinSilver = { ent-TreasureCoinIron } + .desc = { ent-TreasureCoinIron.desc } +ent-TreasureCoinGold = { ent-TreasureCoinIron } + .desc = { ent-TreasureCoinIron.desc } +ent-TreasureCoinAdamantine = { ent-TreasureCoinIron } + .desc = { ent-TreasureCoinIron.desc } +ent-TreasureCoinDiamond = { ent-TreasureCoinIron } + .desc = { ent-TreasureCoinIron.desc } diff --git a/Resources/Locale/ru-RU/traits/traits.ftl b/Resources/Locale/ru-RU/traits/traits.ftl index 59a5e95e6b..f4c8984561 100644 --- a/Resources/Locale/ru-RU/traits/traits.ftl +++ b/Resources/Locale/ru-RU/traits/traits.ftl @@ -31,5 +31,7 @@ trait-liar-name = Патологический лжец trait-liar-desc = Вы с трудом заставляете себя говорить правду. Иногда вы всё равно лжёте. trait-cowboy-name = Ковбойский акцент trait-cowboy-desc = Вы говорите с заметным ковбойским акцентом! +trait-german-name = Немецкий акцент +trait-german-desc = Похоже, вы родом из космической Германии. trait-italian-name = Итальянский акцент trait-italian-desc = Мамма миа! Похоже, вы жили в Космической Италии! diff --git a/Resources/Prototypes/_Sunrise/Disease/Store/Actions.yml b/Resources/Prototypes/_Sunrise/Actions/disease.yml similarity index 63% rename from Resources/Prototypes/_Sunrise/Disease/Store/Actions.yml rename to Resources/Prototypes/_Sunrise/Actions/disease.yml index 7b278e3861..8423af42a9 100644 --- a/Resources/Prototypes/_Sunrise/Disease/Store/Actions.yml +++ b/Resources/Prototypes/_Sunrise/Actions/disease.yml @@ -1,3 +1,88 @@ +- type: entity + id: ActionInfect + name: action-disease-infect-name + description: action-disease-infect-description + noSpawn: true + components: + - type: EntityTargetAction + useDelay: 1 + itemIconStyle: BigAction + whitelist: + components: + - HumanoidAppearance + canTargetSelf: false + interactOnMiss: false + icon: + sprite: _Sunrise/Actions/disease.rsi + state: action + event: !type:InfectEvent + +- type: entity + id: ActionDiseaseShop + name: action-disease-shop-name + description: action-disease-shop-description + noSpawn: true + components: + - type: InstantAction + icon: + sprite: _Sunrise/Actions/disease.rsi + state: shop + event: !type:DiseaseShopActionEvent + +- type: entity + id: ActionDiseaseBaseChance + name: listing-disease-base-chance-name + description: listing-disease-base-chance-description + noSpawn: true + components: + - type: InstantAction + itemIconStyle: NoItem + icon: + sprite: _Sunrise/Actions/disease.rsi + state: baseChance + event: !type:DiseaseAddBaseChanceEvent + +- type: entity + id: ActionDiseaseInfectChance + name: listing-disease-infect-chance-name + description: listing-disease-infect-chance-description + noSpawn: true + components: + - type: InstantAction + itemIconStyle: NoItem + icon: + sprite: _Sunrise/Actions/disease.rsi + state: coughChance + event: !type:DiseaseAddCoughChanceEvent + +- type: entity + id: ActionDiseaseShield + name: listing-disease-shield-name + description: listing-disease-shield-description + noSpawn: true + components: + - type: InstantAction + itemIconStyle: NoItem + icon: + sprite: _Sunrise/Actions/disease.rsi + state: shield + event: !type:DiseaseAddShieldEvent + +- type: entity + id: ActionDiseaseLethal + name: listing-disease-lethal-name + description: listing-disease-lethal-description + noSpawn: true + components: + - type: InstantAction + itemIconStyle: NoItem + icon: + sprite: _Sunrise/Actions/disease.rsi + state: lethal + event: !type:DiseaseAddLethalEvent + +#Store + - type: entity id: ActionDiseaseCough name: listing-disease-cough-name @@ -148,3 +233,4 @@ symptom: "Insult" minLevel: 2 + diff --git a/Resources/Prototypes/_Sunrise/Disease/Store/Listings.yml b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/Disease/Store/Listings.yml rename to Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml diff --git a/Resources/Prototypes/_Sunrise/Disease/Actions.yml b/Resources/Prototypes/_Sunrise/Disease/Actions.yml deleted file mode 100644 index 385d258937..0000000000 --- a/Resources/Prototypes/_Sunrise/Disease/Actions.yml +++ /dev/null @@ -1,82 +0,0 @@ -- type: entity - id: ActionInfect - name: action-disease-infect-name - description: action-disease-infect-description - noSpawn: true - components: - - type: EntityTargetAction - useDelay: 1 - itemIconStyle: BigAction - whitelist: - components: - - HumanoidAppearance - canTargetSelf: false - interactOnMiss: false - icon: - sprite: _Sunrise/Actions/disease.rsi - state: action - event: !type:InfectEvent - -- type: entity - id: ActionDiseaseShop - name: action-disease-shop-name - description: action-disease-shop-description - noSpawn: true - components: - - type: InstantAction - icon: - sprite: _Sunrise/Actions/disease.rsi - state: shop - event: !type:DiseaseShopActionEvent - -- type: entity - id: ActionDiseaseBaseChance - name: listing-disease-base-chance-name - description: listing-disease-base-chance-description - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - icon: - sprite: _Sunrise/Actions/disease.rsi - state: baseChance - event: !type:DiseaseAddBaseChanceEvent - -- type: entity - id: ActionDiseaseInfectChance - name: listing-disease-infect-chance-name - description: listing-disease-infect-chance-description - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - icon: - sprite: _Sunrise/Actions/disease.rsi - state: coughChance - event: !type:DiseaseAddCoughChanceEvent - -- type: entity - id: ActionDiseaseShield - name: listing-disease-shield-name - description: listing-disease-shield-description - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - icon: - sprite: _Sunrise/Actions/disease.rsi - state: shield - event: !type:DiseaseAddShieldEvent - -- type: entity - id: ActionDiseaseLethal - name: listing-disease-lethal-name - description: listing-disease-lethal-description - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - icon: - sprite: _Sunrise/Actions/disease.rsi - state: lethal - event: !type:DiseaseAddLethalEvent diff --git a/Resources/Prototypes/_Sunrise/Disease/Emotes/Emotes.yml b/Resources/Prototypes/_Sunrise/Disease/Emotes/Emotes.yml deleted file mode 100644 index 408cc8158b..0000000000 --- a/Resources/Prototypes/_Sunrise/Disease/Emotes/Emotes.yml +++ /dev/null @@ -1,17 +0,0 @@ -- type: emote - id: Vomit - name: Тошнота - category: Vocal - chatMessages: [тошнитыы] - -- type: emote - id: Headache - name: Головная боль - category: Vocal - chatMessages: [голова болитыы] - -- type: emote - id: Insult - name: Инсульт - category: Vocal - chatMessages: [инсультыы] diff --git a/Resources/Prototypes/_Sunrise/Disease/Store/Categories.yml b/Resources/Prototypes/_Sunrise/Disease/Store/Categories.yml deleted file mode 100644 index fe99baa5a3..0000000000 --- a/Resources/Prototypes/_Sunrise/Disease/Store/Categories.yml +++ /dev/null @@ -1,17 +0,0 @@ -- type: storeCategory - id: DiseaseInfectCategory - name: shop-disease-category-infect - -- type: storeCategory - id: DiseaseSymptomsCategory - name: shop-disease-category-symptoms - -- type: storeCategory - id: DiseaseEvolutionCategory - name: shop-disease-category-evolution - - -- type: currency - id: DiseasePoints - displayName: shop-disease-currency - canWithdraw: false \ No newline at end of file diff --git a/Resources/Prototypes/_Sunrise/Disease/events.yml b/Resources/Prototypes/_Sunrise/Disease/events.yml deleted file mode 100644 index b33170a839..0000000000 --- a/Resources/Prototypes/_Sunrise/Disease/events.yml +++ /dev/null @@ -1,15 +0,0 @@ -- type: entity - parent: BaseGameRule - id: DiseaseSpawn - categories: [ HideSpawnMenu ] - components: - - type: StationEvent - minimumPlayers: 40 - weight: 10 - maxOccurrences: 1 - duration: 1 - earliestStart: 30 - reoccurrenceDelay: 20 - - type: LateJoinSpawnRule - prototype: MobDisease - diff --git a/Resources/Prototypes/_Sunrise/Disease/roundstart.yml b/Resources/Prototypes/_Sunrise/Disease/roundstart.yml deleted file mode 100644 index d707fc5504..0000000000 --- a/Resources/Prototypes/_Sunrise/Disease/roundstart.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: entity - id: DiseaseRule - parent: BaseGameRule - categories: [ HideSpawnMenu ] - components: - - type: DiseaseRule diff --git a/Resources/Prototypes/_Sunrise/EvilTwin/humanoid.yml b/Resources/Prototypes/_Sunrise/Entities/Mobs/Player/eviltwin.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/EvilTwin/humanoid.yml rename to Resources/Prototypes/_Sunrise/Entities/Mobs/Player/eviltwin.yml diff --git a/Resources/Prototypes/_Sunrise/Fugitive/humanoid.yml b/Resources/Prototypes/_Sunrise/Entities/Mobs/Player/fugitive.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/Fugitive/humanoid.yml rename to Resources/Prototypes/_Sunrise/Entities/Mobs/Player/fugitive.yml diff --git a/Resources/Prototypes/_Sunrise/Pacificator/pacificator.yml b/Resources/Prototypes/_Sunrise/Entities/Structures/Machines/pacificator.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/Pacificator/pacificator.yml rename to Resources/Prototypes/_Sunrise/Entities/Structures/Machines/pacificator.yml diff --git a/Resources/Prototypes/_Sunrise/EvilTwin/events.yml b/Resources/Prototypes/_Sunrise/EvilTwin/events.yml deleted file mode 100644 index 918af7993d..0000000000 --- a/Resources/Prototypes/_Sunrise/EvilTwin/events.yml +++ /dev/null @@ -1,11 +0,0 @@ -- type: entity - parent: BaseGameRule - id: EvilTwinSpawn - categories: [ HideSpawnMenu ] - components: - - type: StationEvent - minimumPlayers: 2 - weight: 10 - duration: 1 - - type: LateJoinSpawnRule - prototype: SpawnPointEvilTwin diff --git a/Resources/Prototypes/_Sunrise/EvilTwin/roundstart.yml b/Resources/Prototypes/_Sunrise/EvilTwin/roundstart.yml deleted file mode 100644 index 67ee0932d7..0000000000 --- a/Resources/Prototypes/_Sunrise/EvilTwin/roundstart.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: entity - id: EvilTwin - parent: BaseGameRule - categories: [ HideSpawnMenu ] - components: - - type: EvilTwinRule diff --git a/Resources/Prototypes/_Sunrise/Fugitive/events.yml b/Resources/Prototypes/_Sunrise/Fugitive/events.yml deleted file mode 100644 index 60c77a4c18..0000000000 --- a/Resources/Prototypes/_Sunrise/Fugitive/events.yml +++ /dev/null @@ -1,12 +0,0 @@ -- type: entity - parent: BaseGameRule - id: FugitiveSpawn - categories: [ HideSpawnMenu ] - components: - - type: StationEvent - weight: 10 - duration: 1 - earliestStart: 30 - minimumPlayers: 10 - - type: LateJoinSpawnRule - prototype: SpawnPointGhostFugitive diff --git a/Resources/Prototypes/_Sunrise/Fugitive/roundstart.yml b/Resources/Prototypes/_Sunrise/Fugitive/roundstart.yml deleted file mode 100644 index ad99c4a357..0000000000 --- a/Resources/Prototypes/_Sunrise/Fugitive/roundstart.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: entity - id: Fugitive - parent: BaseGameRule - categories: [ HideSpawnMenu ] - components: - - type: FugitiveRule diff --git a/Resources/Prototypes/_Sunrise/GameRules/events.yml b/Resources/Prototypes/_Sunrise/GameRules/events.yml new file mode 100644 index 0000000000..02d1183dfc --- /dev/null +++ b/Resources/Prototypes/_Sunrise/GameRules/events.yml @@ -0,0 +1,39 @@ +- type: entity + parent: BaseGameRule + id: DiseaseSpawn + categories: [ HideSpawnMenu ] + components: + - type: StationEvent + minimumPlayers: 40 + weight: 10 + maxOccurrences: 1 + duration: 1 + earliestStart: 30 + reoccurrenceDelay: 20 + - type: LateJoinSpawnRule + prototype: MobDisease + +- type: entity + parent: BaseGameRule + id: FugitiveSpawn + categories: [ HideSpawnMenu ] + components: + - type: StationEvent + weight: 10 + duration: 1 + earliestStart: 30 + minimumPlayers: 10 + - type: LateJoinSpawnRule + prototype: SpawnPointGhostFugitive + +- type: entity + parent: BaseGameRule + id: EvilTwinSpawn + categories: [ HideSpawnMenu ] + components: + - type: StationEvent + minimumPlayers: 2 + weight: 10 + duration: 1 + - type: LateJoinSpawnRule + prototype: SpawnPointEvilTwin diff --git a/Resources/Prototypes/_Sunrise/GameRules/roundstart.yml b/Resources/Prototypes/_Sunrise/GameRules/roundstart.yml new file mode 100644 index 0000000000..3bc32e19ba --- /dev/null +++ b/Resources/Prototypes/_Sunrise/GameRules/roundstart.yml @@ -0,0 +1,20 @@ +- type: entity + id: DiseaseRule + parent: BaseGameRule + categories: [ HideSpawnMenu ] + components: + - type: DiseaseRule + +- type: entity + id: Fugitive + parent: BaseGameRule + categories: [ HideSpawnMenu ] + components: + - type: FugitiveRule + +- type: entity + id: EvilTwin + parent: BaseGameRule + categories: [ HideSpawnMenu ] + components: + - type: EvilTwinRule diff --git a/Resources/Prototypes/_Sunrise/Loadouts/Jobs/Blueshield/blueshield.yml b/Resources/Prototypes/_Sunrise/Loadouts/Jobs/Blueshield/blueshield.yml index ec3bb97c48..45d1d6976c 100644 --- a/Resources/Prototypes/_Sunrise/Loadouts/Jobs/Blueshield/blueshield.yml +++ b/Resources/Prototypes/_Sunrise/Loadouts/Jobs/Blueshield/blueshield.yml @@ -11,7 +11,7 @@ - type: loadout id: BlueShieldDuffel equipment: - jumpsuit: ClothingBackpackDuffelBlueShield + back: ClothingBackpackDuffelBlueShield - type: loadout id: BlueShieldJumpskirt diff --git a/Resources/Prototypes/_Sunrise/EvilTwin/objective.yml b/Resources/Prototypes/_Sunrise/Objectives/eviltwin.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/EvilTwin/objective.yml rename to Resources/Prototypes/_Sunrise/Objectives/eviltwin.yml diff --git a/Resources/Prototypes/_Sunrise/Fugitive/objective.yml b/Resources/Prototypes/_Sunrise/Objectives/fugitive.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/Fugitive/objective.yml rename to Resources/Prototypes/_Sunrise/Objectives/fugitive.yml diff --git a/Resources/Prototypes/_Sunrise/Disease/DiseaseRole.yml b/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/Disease/DiseaseRole.yml rename to Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml diff --git a/Resources/Prototypes/_Sunrise/EvilTwin/antag.yml b/Resources/Prototypes/_Sunrise/Roles/Antags/eviltwin.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/EvilTwin/antag.yml rename to Resources/Prototypes/_Sunrise/Roles/Antags/eviltwin.yml diff --git a/Resources/Prototypes/_Sunrise/Fugitive/antag.yml b/Resources/Prototypes/_Sunrise/Roles/Antags/fugitive.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/Fugitive/antag.yml rename to Resources/Prototypes/_Sunrise/Roles/Antags/fugitive.yml diff --git a/Resources/Prototypes/_Sunrise/Store/categories.yml b/Resources/Prototypes/_Sunrise/Store/categories.yml index 70bf2dc716..a36fb767f3 100644 --- a/Resources/Prototypes/_Sunrise/Store/categories.yml +++ b/Resources/Prototypes/_Sunrise/Store/categories.yml @@ -92,3 +92,21 @@ id: SecurityThings name: Служба Безопасности priority: 5 + +- type: storeCategory + id: DiseaseInfectCategory + name: shop-disease-category-infect + +- type: storeCategory + id: DiseaseSymptomsCategory + name: shop-disease-category-symptoms + +- type: storeCategory + id: DiseaseEvolutionCategory + name: shop-disease-category-evolution + + +- type: currency + id: DiseasePoints + displayName: shop-disease-currency + canWithdraw: false diff --git a/Resources/Prototypes/_Sunrise/Disease/Emotes/AutoEmotes.yml b/Resources/Prototypes/_Sunrise/Voice/auto_emotes.yml similarity index 100% rename from Resources/Prototypes/_Sunrise/Disease/Emotes/AutoEmotes.yml rename to Resources/Prototypes/_Sunrise/Voice/auto_emotes.yml diff --git a/Resources/Prototypes/_Sunrise/Voice/speech_emotes.yml b/Resources/Prototypes/_Sunrise/Voice/speech_emotes.yml index 79231fa8a8..c6ae9dc536 100644 --- a/Resources/Prototypes/_Sunrise/Voice/speech_emotes.yml +++ b/Resources/Prototypes/_Sunrise/Voice/speech_emotes.yml @@ -189,3 +189,21 @@ - хряк. - хряк - хряк! + +- type: emote + id: Vomit + name: Тошнота + category: Vocal + chatMessages: [тошнитыы] + +- type: emote + id: Headache + name: Головная боль + category: Vocal + chatMessages: [голова болитыы] + +- type: emote + id: Insult + name: Инсульт + category: Vocal + chatMessages: [инсультыы] diff --git a/Tools/_sunrise/Report.txt b/Tools/_sunrise/Report.txt new file mode 100644 index 0000000000..9dff0137af --- /dev/null +++ b/Tools/_sunrise/Report.txt @@ -0,0 +1,71 @@ + +Проверка локализации: +Не переведённая строка "accent-german-words-replace-1" в "accent\german.ftl", строка 2: accent-german-words-replace-1 = ja +Не переведённая строка "accent-german-words-replace-2" в "accent\german.ftl", строка 4: accent-german-words-replace-2 = nein +Не переведённая строка "accent-german-words-replace-3" в "accent\german.ftl", строка 6: accent-german-words-replace-3 = Das +Не переведённая строка "accent-german-words-replace-4" в "accent\german.ftl", строка 8: accent-german-words-replace-4 = scheisse +Не переведённая строка "accent-german-words-replace-5" в "accent\german.ftl", строка 10: accent-german-words-replace-5 = wurst +Не переведённая строка "accent-german-words-replace-6" в "accent\german.ftl", строка 12: accent-german-words-replace-6 = würste +Не переведённая строка "accent-german-words-replace-7" в "accent\german.ftl", строка 14: accent-german-words-replace-7 = mann +Не переведённая строка "accent-german-words-replace-8" в "accent\german.ftl", строка 16: accent-german-words-replace-8 = männer +Не переведённая строка "accent-german-words-9-2" в "accent\german.ftl", строка 18: accent-german-words-9-2 = lady +Не переведённая строка "accent-german-words-replace-9" в "accent\german.ftl", строка 19: accent-german-words-replace-9 = frau +Не переведённая строка "accent-german-words-10-2" в "accent\german.ftl", строка 21: accent-german-words-10-2 = ladies +Не переведённая строка "accent-german-words-replace-10" в "accent\german.ftl", строка 22: accent-german-words-replace-10 = frauen +Не переведённая строка "accent-german-words-replace-11" в "accent\german.ftl", строка 24: accent-german-words-replace-11 = herr +Не переведённая строка "accent-german-words-replace-12" в "accent\german.ftl", строка 26: accent-german-words-replace-12 = herren +Не переведённая строка "accent-german-words-replace-13" в "accent\german.ftl", строка 28: accent-german-words-replace-13 = mein gott +Не переведённая строка "accent-german-words-replace-14" в "accent\german.ftl", строка 30: accent-german-words-replace-14 = mein +Не переведённая строка "accent-german-words-replace-15" в "accent\german.ftl", строка 32: accent-german-words-replace-15 = hier +Не переведённая строка "accent-german-words-replace-16" в "accent\german.ftl", строка 34: accent-german-words-replace-16 = dummkopf +Не переведённая строка "accent-german-words-replace-17" в "accent\german.ftl", строка 36: accent-german-words-replace-17 = dummköpfe +Не переведённая строка "accent-german-words-replace-18" в "accent\german.ftl", строка 38: accent-german-words-replace-18 = schmetterling +Не переведённая строка "accent-german-words-replace-19" в "accent\german.ftl", строка 40: accent-german-words-replace-19 = maschine +Не переведённая строка "accent-german-words-replace-19-2" в "accent\german.ftl", строка 41: accent-german-words-replace-19-2 = auto +Не переведённая строка "accent-german-words-replace-20" в "accent\german.ftl", строка 43: accent-german-words-replace-20 = maschinen +Не переведённая строка "accent-german-words-replace-21" в "accent\german.ftl", строка 45: accent-german-words-replace-21 = achtung +Не переведённая строка "accent-german-words-replace-22" в "accent\german.ftl", строка 47: accent-german-words-replace-22 = musik +Не переведённая строка "accent-german-words-replace-23" в "accent\german.ftl", строка 49: accent-german-words-replace-23 = kapitän +Не переведённая строка "accent-german-words-replace-24" в "accent\german.ftl", строка 51: accent-german-words-replace-24 = döner +Не переведённая строка "accent-german-words-replace-25" в "accent\german.ftl", строка 53: accent-german-words-replace-25 = maus +Не переведённая строка "accent-german-words-replace-26" в "accent\german.ftl", строка 55: accent-german-words-replace-26 = wat +Не переведённая строка "accent-german-words-replace-27" в "accent\german.ftl", строка 57: accent-german-words-replace-27 = dankeschön +Не переведённая строка "accent-german-words-replace-28" в "accent\german.ftl", строка 59: accent-german-words-replace-28 = danke +Не переведённая строка "accent-german-words-replace-29" в "accent\german.ftl", строка 61: accent-german-words-replace-29 = gesundheit +Не переведённая строка "accent-german-words-replace-30" в "accent\german.ftl", строка 63: accent-german-words-replace-30 = flammenwerfer +Не переведённая строка "accent-german-words-replace-31" в "accent\german.ftl", строка 65: accent-german-words-replace-31 = poltergeist +Не переведённая строка "accent-german-words-32-2" в "accent\german.ftl", строка 67: accent-german-words-32-2 = cabbage +Не переведённая строка "accent-german-words-replace-32" в "accent\german.ftl", строка 68: accent-german-words-replace-32 = kraut +Не переведённая строка "accent-german-words-replace-33" в "accent\german.ftl", строка 70: accent-german-words-replace-33 = wodka +Не переведённая строка "accent-german-words-replace-34" в "accent\german.ftl", строка 72: accent-german-words-replace-34 = rucksack +Не переведённая строка "accent-german-words-replace-35" в "accent\german.ftl", строка 74: accent-german-words-replace-35 = medizin +Не переведённая строка "accent-german-words-replace-36" в "accent\german.ftl", строка 76: accent-german-words-replace-36 = akzent +Не переведённая строка "accent-german-words-replace-37" в "accent\german.ftl", строка 78: accent-german-words-replace-37 = anomalie +Не переведённая строка "accent-german-words-38-2" в "accent\german.ftl", строка 80: accent-german-words-38-2 = artefact +Не переведённая строка "accent-german-words-replace-38" в "accent\german.ftl", строка 81: accent-german-words-replace-38 = artefakt +Не переведённая строка "accent-german-words-replace-39" в "accent\german.ftl", строка 83: accent-german-words-replace-39 = dumm +Не переведённая строка "accent-german-words-replace-40" в "accent\german.ftl", строка 85: accent-german-words-replace-40 = doof +Не переведённая строка "accent-german-words-replace-41" в "accent\german.ftl", строка 88: accent-german-words-replace-41 = wunderbar +Не переведённая строка "accent-german-words-replace-42" в "accent\german.ftl", строка 90: accent-german-words-replace-42 = warnung +Не переведённая строка "accent-german-words-replace-43" в "accent\german.ftl", строка 92: accent-german-words-replace-43 = warnungen +Не переведённая строка "accent-german-words-replace-44" в "accent\german.ftl", строка 94: accent-german-words-replace-44 = und +Не переведённая строка "accent-german-words-replace-45" в "accent\german.ftl", строка 96: accent-german-words-replace-45 = karpfen +Не переведённая строка "accent-german-words-replace-46" в "accent\german.ftl", строка 98: accent-german-words-replace-46 = kommandant +Не переведённая строка "accent-german-words-replace-47" в "accent\german.ftl", строка 100: accent-german-words-replace-47 = bier +Не переведённая строка "accent-german-words-replace-48" в "accent\german.ftl", строка 102: accent-german-words-replace-48 = hallo +Не переведённая строка "accent-german-words-replace-49" в "accent\german.ftl", строка 104: accent-german-words-replace-49 = guten Tag +Не переведённая строка "accent-german-words-replace-50" в "accent\german.ftl", строка 106: accent-german-words-replace-50 = hilfe +Не переведённая строка "accent-german-words-replace-51" в "accent\german.ftl", строка 108: accent-german-words-replace-51 = auf Wiedersehen +Не переведённая строка "accent-german-words-replace-52" в "accent\german.ftl", строка 110: accent-german-words-replace-52 = tschüss +Не переведённая строка "accent-german-words-replace-53" в "accent\german.ftl", строка 113: accent-german-words-replace-53 = tschau +Не переведённая строка "accent-german-words-replace-54" в "accent\german.ftl", строка 115: accent-german-words-replace-54 = fantastisch +Не переведённая строка "accent-german-words-replace-55" в "accent\german.ftl", строка 117: accent-german-words-replace-55 = doppelgänger +Не переведённая строка "accent-german-words-replace-56" в "accent\german.ftl", строка 119: accent-german-words-replace-56 = verboten +Не переведённая строка "accent-german-words-replace-57" в "accent\german.ftl", строка 122: accent-german-words-replace-57 = schnell +Не переведённая строка "accent-german-words-replace-58" в "accent\german.ftl", строка 124: accent-german-words-replace-58 = krankenhaus +Не переведённая строка "accent-german-words-replace-59" в "accent\german.ftl", строка 126: accent-german-words-replace-59 = tesla coil +Не переведённая строка "accent-german-words-replace-60" в "accent\german.ftl", строка 128: accent-german-words-replace-60 = tesla coils +Не переведённая строка "accent-german-words-61" в "accent\german.ftl", строка 129: accent-german-words-61 = teslaloose +Не переведённая строка "accent-german-words-61-2" в "accent\german.ftl", строка 130: accent-german-words-61-2 = tesloose +Не переведённая строка "accent-german-words-61-3" в "accent\german.ftl", строка 131: accent-german-words-61-3 = lightning ball +Не переведённая строка "accent-german-words-replace-61" в "accent\german.ftl", строка 134: accent-german-words-replace-61 = kugelblitz diff --git a/Tools/_sunrise/__init__.py b/Tools/_sunrise/__init__.py new file mode 100644 index 0000000000..8fc809dcf5 --- /dev/null +++ b/Tools/_sunrise/__init__.py @@ -0,0 +1 @@ +from fluentformatter import FluentFile, FluentFormatter diff --git a/Tools/_sunrise/en_syntax_finder.py b/Tools/_sunrise/en_syntax_finder.py new file mode 100644 index 0000000000..d9de97079f --- /dev/null +++ b/Tools/_sunrise/en_syntax_finder.py @@ -0,0 +1,230 @@ +import os +import re +import sys + +def is_english(text): + return bool(re.search('[a-zA-Z]', text)) + +def has_russian(text): + return bool(re.search('[а-яА-Я]', text)) + +def remove_braces_content(text): + text1 = re.sub(r'\{.*?\}', '', text) + text2 = re.sub(r'\[.*?\]', '', text1) + text3 = re.sub(r'\<.*?\>', '', text2) + return text3 + +def contains_ignored_word(text, ignore_list): + return any(ignored_word in text for ignored_word in ignore_list) + +def check_translations(root_dir, report_file, ignore_list, ignore_files): + root_dir_abs = os.path.abspath(root_dir) + found_issues = False + localization_issues = [] + + for dirpath, _, filenames in os.walk(root_dir): + for filename in filenames: + if filename.endswith('.ftl'): + file_path = os.path.join(dirpath, filename) + rel_path = os.path.relpath(file_path, root_dir_abs) + + # Проверка, должен ли файл быть игнорирован + if filename in ignore_files: + print(f'Игнорирование файла: {filename}') + continue + + with open(file_path, 'r', encoding='utf-8') as ftl_file: + lines = ftl_file.readlines() + for line_num, line in enumerate(lines, start=1): + if '=' in line and not line.strip().startswith('#'): + key, value = line.split('=', 1) + key = key.strip() + value = value.strip() + value = remove_braces_content(value) + + if not has_russian(value) and not contains_ignored_word(value, ignore_list): + if key.endswith('.desc') or key.endswith('.suffix'): + if is_english(value): + localization_issues.append(f'Не переведённая строка "{key}" в "{rel_path}", строка {line_num}: {line.strip()}\n') + elif is_english(value): + localization_issues.append(f'Не переведённая строка "{key}" в "{rel_path}", строка {line_num}: {line.strip()}\n') + + if localization_issues: + found_issues = True + with open(report_file, 'a', encoding='utf-8') as report: + report.write("\nПроверка локализации:\n") + report.writelines(localization_issues) + + return found_issues + +def check_prototypes_for_russian(root_dir, report_file): + root_dir_abs = os.path.abspath(root_dir) + found_issues = False + prototype_issues = [] + + for dirpath, _, filenames in os.walk(root_dir): + for filename in filenames: + if filename.endswith('.yml') or filename.endswith('.yaml'): + file_path = os.path.join(dirpath, filename) + rel_path = os.path.relpath(file_path, root_dir_abs) + with open(file_path, 'r', encoding='utf-8') as prototype_file: + lines = prototype_file.readlines() + for line_num, line in enumerate(lines, start=1): + if has_russian(line): + prototype_issues.append(f'Строка с русскими символами в "{rel_path}", строка {line_num}: {line.strip()}\n') + + if prototype_issues: + found_issues = True + with open(report_file, 'a', encoding='utf-8') as report: + report.write("\nПроверка прототипов:\n") + report.writelines(prototype_issues) + + return found_issues + +if __name__ == "__main__": + # Список слов или фраз, которые нужно игнорировать + ignore_list = [ + 'EntityUid', + 'Zzz...', + 'playglobalsound', + 'variantize', + 'ID', + 'IP', + 'TileY', + 'TileX', + 'X', 'Y', + 'EI NATH', + '>MFW', + 'Hello world!', + 'green', 'red', + 'purple', + 'yellow', + 'orange', + '$count', + 'v1', + 'Nanotrasen', + 'float', + '$open', + '$rate', + 'LV-426', + 'LOOC', + 'OOC', + 'white', + 'blue', + 'grey', + '#1b487e', + '#B50F1D', + 'Never gonna let you down!', + 'Never gonna give you up!', + 'power_net_query', + 'zzz...', + 'GENDER', + '$initialCount', + 'Github', + 'GitHub', + 'Space Station 14', + 'AHelp', + 'TARCOL MINTI ZHERI', + "ONI'SOMA!", + 'AIE KHUSE EU', + 'Gray', + '$enabled', + 'FOO-BAR-BAZ', + '$state', + 'Float', + 'Integer', + 'left-4-zed', + 'plant-B-gone', + 'Pwr Game', + 'sol dry', + 'gray', + 'UITest2', + 'EVA', + 'MK58', + 'WT550', + 'N1984', + 'DEUS VULT', + 'AMS-42', + 'SAM-300', + 'AJ-100', + 'Deus vult! Ave maria!', + 'TR-263', + 'G-Man', + 'RGB', + 'Changelog', + 'M1 Garand', + 'BR-64', + 'MG-42', + 'MG-60', + 'RPD', + "Monkin' Donuts", + 'P-90', + 'MP-38', + 'MP5', + 'MP7', + 'PPSH-41', + 'AKMS', + 'AK74-U', + 'G-36', + 'M-28', + 'AR-18', + 'M16A4', + 'STG 44', + 'ACP-14', + 'Glock-22', + 'GL-79', + 'M-41', + 'DEAD-BEEF', + 'UwU', + 'C-4', + 'Who ya gonna call?', + 'Ue No', + 'Carpe diem!', + 'Space-Up!', + 'Sun-kist', + 'Robust Softdrinks', + 'C-20r', + 'Donut Corp.', + 'LV426', + 'Kept ya waiting, huh?', + 'Getmore Chocolate Corp', + 'Discount Dan', + 'L6 SAW', + 'L6C ROW', + 'China Lake', + 'Plant-B-Gone', + 'Bon appétit!', + 'Bon ap-petite!', + 'Sol dry', + 'hover entity', # Спорная хуйня, проверить в игре + 'drag shadow', # Спорная хуйня, проверить в игре + 'dbg_rotation1', + 'dbg_rotation4', + 'dbg_rotationTex', + 'plague inc 2.0', + 'False', + 'True', + 'chèvre' + ] # Добавьте сюда слова, которые нужно игнорировать + + # Список файлов, которые нужно игнорировать + ignore_files = ['italian.ftl', 'popup.ftl', 'controls.ftl', 'input.ftl', 'speech-chatsan.ftl', 'speech-liar.ftl', 'russian.ftl'] # Добавьте сюда имена файлов, которые нужно игнорировать + + # Получаем директорию, где находится скрипт + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # Устанавливаем корневую директорию и файл отчета + localization_directory = os.path.join(script_dir, '../../Resources/Locale/ru-RU') + prototypes_directory = os.path.join(script_dir, '../../Resources/Prototypes') + report_path = os.path.join(script_dir, 'Report.txt') + + # Создаём/очищаем файл отчёта + open(report_path, 'w').close() + + # Проверяем локализацию + localization_issues_found = check_translations(localization_directory, report_path, ignore_list, ignore_files) + + # Проверяем прототипы на наличие русских символов + #prototypes_issues_found = check_prototypes_for_russian(prototypes_directory, report_path) + + print(f"Отчет создан в {report_path}") \ No newline at end of file diff --git a/Tools/_sunrise/file.py b/Tools/_sunrise/file.py new file mode 100644 index 0000000000..bbb53c79b1 --- /dev/null +++ b/Tools/_sunrise/file.py @@ -0,0 +1,91 @@ +import typing + +from fluent.syntax import ast +from yamlmodels import YAMLElements +import os + + +class File: + def __init__(self, full_path): + self.full_path = full_path + + def read_data(self): + file = open(self.full_path, 'r', encoding='utf8') + # replace необходим для того, чтобы 1-е сообщение не считалось ast.Junk + file_data = file.read().replace('', '') + file.close() + + return file_data + + def save_data(self, file_data: typing.AnyStr): + os.makedirs(os.path.dirname(self.full_path), exist_ok=True) + file = open(self.full_path, 'w', encoding='utf8') + file.write(file_data) + file.close() + + def get_relative_path(self, base_path): + return os.path.relpath(self.full_path, base_path) + + def get_relative_path_without_extension(self, base_path): + return self.get_relative_path(base_path).split('.', maxsplit=1)[0] + + def get_relative_parent_dir(self, base_path): + return os.path.relpath(self.get_parent_dir(), base_path) + + def get_parent_dir(self): + return os.path.dirname(self.full_path) + + def get_name(self): + return os.path.basename(self.full_path).split('.')[0] + + +class FluentFile(File): + def __init__(self, full_path): + super().__init__(full_path) + self.full_path = full_path + + def parse_data(self, file_data: typing.AnyStr): + from fluent.syntax import FluentParser + + return FluentParser().parse(file_data) + + def serialize_data(self, parsed_file_data: ast.Resource): + from fluent.syntax import FluentSerializer + + return FluentSerializer(with_junk=True).serialize(parsed_file_data) + + def read_serialized_data(self): + return self.serialize_data(self.parse_data(self.read_data())) + + def read_parsed_data(self): + return self.parse_data(self.read_data()) + + +class YAMLFluentFileAdapter(File): + def __init__(self, full_path): + super().__init__(full_path) + self.full_path = full_path + + # def create_fluent_from_yaml_elements(self, yaml_elements): + + + +class YAMLFile(File): + def __init__(self, full_path): + super().__init__(full_path) + + def parse_data(self, file_data: typing.AnyStr): + import yaml + + return yaml.load(file_data, Loader=yaml.BaseLoader) + + def get_elements(self, parsed_data): + + if isinstance(parsed_data, list): + elements = YAMLElements(parsed_data).elements + # элемент может быть None, если имеет неизвестный тип + exist_elements = list(filter(lambda el: el, elements)) + + return exist_elements + + return [] diff --git a/Tools/_sunrise/fluentast.py b/Tools/_sunrise/fluentast.py new file mode 100644 index 0000000000..20576b4705 --- /dev/null +++ b/Tools/_sunrise/fluentast.py @@ -0,0 +1,187 @@ +import typing + +from fluent.syntax import ast, FluentParser, FluentSerializer +from lokalisemodels import LokaliseKey +from pydash import py_ + + +class FluentAstAbstract: + element = None + @classmethod + def get_id_name(cls, element): + if isinstance(element, ast.Junk): + return FluentAstJunk(element).get_id_name() + elif isinstance(element, ast.Message): + return FluentAstMessage(element).get_id_name() + elif isinstance(element, ast.Term): + return FluentAstTerm(element).get_id_name() + else: + return None + + @classmethod + def create_element(cls, element): + if isinstance(element, ast.Junk): + cls.element = FluentAstJunk(element) + return cls.element + elif isinstance(element, ast.Message): + cls.element = FluentAstMessage(element) + return cls.element + elif isinstance(element, ast.Term): + cls.element = FluentAstTerm(element) + return cls.element + else: + return None + + +class FluentAstMessage: + def __init__(self, message: ast.Message): + self.message = message + self.element = message + + def get_id_name(self): + return self.message.id.name + + +class FluentAstTerm: + def __init__(self, term: ast.Term): + self.term = term + self.element = term + + def get_id_name(self): + return self.term.id.name + + +class FluentAstAttribute: + def __init__(self, id, value, parent_key = None): + self.id = id + self.value = value + self.parent_key = parent_key + + +class FluentAstAttributeFactory: + @classmethod + def from_yaml_element(cls, element): + attrs = [] + if element.description: + attrs.append(FluentAstAttribute('desc', element.description)) + + if element.suffix: + attrs.append(FluentAstAttribute('suffix', element.suffix)) + + if not len(attrs): + return None + + return attrs + + +class FluentAstJunk: + def __init__(self, junk: ast.Junk): + self.junk = junk + self.element = junk + + def get_id_name(self): + return self.junk.content.split('=')[0].strip() + + +class FluentSerializedMessage: + @classmethod + def from_yaml_element(cls, id, value, attributes, parent_id = None, raw_key = False): + if not value and not id and not parent_id: + return None + + if not attributes: + attributes = [] + + if len(list(filter(lambda attr: attr.id == 'desc', attributes))) == 0: + if parent_id: + attributes.append(FluentAstAttribute('desc', '{ ' + FluentSerializedMessage.get_key(parent_id) + '.desc' + ' }')); + else: + attributes.append(FluentAstAttribute('desc', '{ "" }')) + + message = f'{cls.get_key(id, raw_key)} = {cls.get_value(value, parent_id)}\n' + + if attributes and len(attributes): + full_message = message + + for attr in attributes: + fluent_newlines = attr.value.replace("\n", "\n "); + full_message = cls.add_attr(full_message, attr.id, fluent_newlines, raw_key=raw_key) + + desc_attr = py_.find(attributes, lambda a: a.id == 'desc') + if not desc_attr and parent_id: + full_message = cls.add_attr(full_message, 'desc', '{ ' + FluentSerializedMessage.get_key(parent_id) + '.desc' + ' }') + + return full_message + + return cls.to_serialized_message(message) + + @classmethod + def from_lokalise_keys(cls, keys: typing.List[LokaliseKey]): + attributes_keys = list(filter(lambda k: k.is_attr, keys)) + attributes = list(map(lambda k: FluentAstAttribute(id='.{name}'.format(name=k.get_key_last_name(k.key_name)), + value=FluentSerializedMessage.get_attr(k, k.get_key_last_name(k.key_name)), parent_key=k.get_parent_key()), + attributes_keys)) + attributes_group = py_.group_by(attributes, 'parent_key') + + serialized_message = '' + for key in keys: + if key.is_attr: + continue + key_name = key.get_key_last_name(key.key_name) + key_value = key.get_translation('ru').data['translation'] + key_attributes = [] + + if len(attributes_group): + k = f'{key.get_key_base_name(key.key_name)}.{key_name}' + key_attributes = attributes_group[k] if k in attributes_group else [] + + message = key.serialize_message() + full_message = cls.from_yaml_element(key_name, key_value, key_attributes, key.get_parent_key(), True) + + if full_message: + serialized_message = serialized_message + '\n' + full_message + elif message: + serialized_message = serialized_message + '\n' + message + else: + raise Exception('Что-то пошло не так') + + return serialized_message + + @staticmethod + def get_attr(k, name, parent_id = None): + if parent_id: + return "{ " + parent_id + f'.{name}' + " }" + else: + return k.get_translation('ru').data['translation'] + + + @staticmethod + def to_serialized_message(string_message): + if not string_message: + return None + + ast_message = FluentParser().parse(string_message) + serialized = FluentSerializer(with_junk=True).serialize(ast_message) + + return serialized if serialized else '' + + @staticmethod + def add_attr(message_str, attr_key, attr_value, raw_key = False): + prefix = '' if raw_key else '.' + return f'{message_str}\n {prefix}{attr_key} = {attr_value}' + + @staticmethod + def get_value(value, parent_id): + if value: + return value + elif parent_id: + return '{ ' + FluentSerializedMessage.get_key(parent_id) + ' }' + else: + return '{ "" }' + + @staticmethod + def get_key(id, raw = False): + if raw: + return f'{id}' + else: + return f'ent-{id}' diff --git a/Tools/_sunrise/fluentastcomparer.py b/Tools/_sunrise/fluentastcomparer.py new file mode 100644 index 0000000000..21fa970175 --- /dev/null +++ b/Tools/_sunrise/fluentastcomparer.py @@ -0,0 +1,100 @@ +from fluent.syntax import ast +from fluentast import FluentAstAbstract +from pydash import py_ + + +class FluentAstComparer: + def __init__(self, sourse_parsed: ast.Resource, target_parsed: ast.Resource): + self.sourse_parsed = sourse_parsed + self.target_parsed = target_parsed + self.source_elements = list( + filter(lambda el: el, list(map(lambda e: FluentAstAbstract.create_element(e), sourse_parsed.body)))) + self.target_elements = list( + filter(lambda el: el, list(map(lambda e: FluentAstAbstract.create_element(e), target_parsed.body)))) + + # Возвращает полностью эквивалентные сообщения (не считая span) + def get_equal_elements(self): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span']) + + return py_.intersection_with(self.source_elements, self.target_elements, comparator=comparator) + + # Возвращает полностью неэквивалентные сообщения (не считая span) + def get_not_equal_elements(self): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span']) + diff = py_.difference_with(self.source_elements, self.target_elements, comparator=comparator) + + return diff + + # Возвращает сообщения с эквивалентными именами ключей + def get_equal_id_names(self): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span', 'value', 'comment', 'attributes']) + eq = py_.intersection_with(self.source_elements, self.target_elements, comparator=comparator) + + return eq + + # Возвращает сообщения с неэквивалентными именами ключей + def get_not_equal_id_names(self): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span', 'value', 'comment', 'attributes']) + diff = py_.difference_with(self.source_elements, self.target_elements, comparator=comparator) + + return diff + + # Возвращает сообщения target, существующие в source + def get_exist_id_names(self, source, target): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span', 'value', 'comment', 'attributes']) + eq = py_.intersection_with(source, target, comparator=comparator) + + return eq + + # Возвращает сообщения target, существующие в source + def get_not_exist_id_names(self): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span', 'value', 'comment', 'attributes']) + diff = py_.difference_with(self.target_elements, self.source_elements, comparator=comparator) + + return diff + + # Возвращает сообщения с эквивалентным значением и атрибутами + def get_equal_values_with_attrs(self): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span', 'id', 'comment']) + eq = py_.intersection_with(self.target_elements, self.source_elements, comparator=comparator) + + return eq + + # Возвращает сообщения из source с неэквивалентным значением и атрибутами + def get_not_equal_values_with_attrs(self): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span', 'id', 'comment']) + diff = py_.difference_with(self.source_elements, self.target_elements, + comparator=lambda a, b: a.element.equals(b.element, + ignored_fields=['span', 'id', 'comment'])) + + return diff + + # Возвращает сообщения из source, существующие в target и source, с неэквивалентным значением и атрибутами + def get_not_equal_exist_values_with_attrs(self): + diff = py_.difference_with(self.source_elements, self.target_elements, + comparator=lambda a, b: a.element.equals(b.element, + ignored_fields=['span', 'id', 'comment'])) + ex = self.get_exist_id_names(self.source_elements, self.target_elements) + exist = py_.intersection(diff, ex) + + return exist + + # Возвращает сообщения из target с неэквивалентным значением и атрибутами + + def get_target_not_equal_values_with_attrs(self): + comparator = lambda a, b: a.element.equals(b.element, ignored_fields=['span', 'id', 'comment']) + diff = py_.difference_with(self.source_elements, self.target_elements, comparator=comparator) + + return diff + + # Возвращает сообщения, существующие в target и source, с неэквивалентным значением и атрибутами + def get_target_not_equal_exist_values_with_attrs(self): + diff = py_.difference_with(self.target_elements, self.source_elements, + comparator=lambda a, b: a.element.equals(b.element, + ignored_fields=['span', 'id', 'comment'])) + exist = py_.intersection(diff, self.get_exist_id_names(self.target_elements, self.source_elements)) + + return exist + + def find_message_by_id_name(self, id_name, list): + return py_.find(list, lambda el: el.get_id_name() == id_name) diff --git a/Tools/_sunrise/fluentastmanager.py b/Tools/_sunrise/fluentastmanager.py new file mode 100644 index 0000000000..76cf4f7bcd --- /dev/null +++ b/Tools/_sunrise/fluentastmanager.py @@ -0,0 +1,25 @@ +from fluent.syntax import ast +from fluentast import FluentAstAbstract + + +class FluentAstManager: + def __init__(self, sourse_parsed: ast.Resource, target_parsed: ast.Resource): + self.sourse_parsed = sourse_parsed + self.target_parsed = target_parsed + self.source_elements = list(map(lambda e: FluentAstAbstract.create_element(e), sourse_parsed.body)) + self.target_elements = list(map(lambda e: FluentAstAbstract.create_element(e), target_parsed.body)) + + def update_by_index(self, index, update_element: ast.Message): + source_element = None + + try: + source_element = self.sourse_parsed.body[index] + except: + raise Exception(f'Нет элемента с индексом {index}') + + if not source_element: + raise Exception(f'Элемен с индексом {index} не существует') + + self.sourse_parsed.body[index] = update_element + + return self.sourse_parsed diff --git a/Tools/_sunrise/fluentformatter.py b/Tools/_sunrise/fluentformatter.py new file mode 100644 index 0000000000..f7a96dbb6c --- /dev/null +++ b/Tools/_sunrise/fluentformatter.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# Форматтер, приводящий fluent-файлы (.ftl) в соответствие стайлгайду +# path - путь к папке, содержащий форматируемые файлы. Для форматирования всего проекта, необходимо заменить значение на root_dir_path +import typing + +from file import FluentFile +from project import Project +from fluent.syntax import ast, FluentParser, FluentSerializer + + +######################################### Class defifitions ############################################################ + +class FluentFormatter: + @classmethod + def format(cls, fluent_files: typing.List[FluentFile]): + for file in fluent_files: + file_data = file.read_data() + parsed_file_data = file.parse_data(file_data) + serialized_file_data = file.serialize_data(parsed_file_data) + file.save_data(serialized_file_data) + + @classmethod + def format_serialized_file_data(cls, file_data: typing.AnyStr): + parsed_data = FluentParser().parse(file_data) + + return FluentSerializer(with_junk=True).serialize(parsed_data) + + + +######################################## Var definitions ############################################################### +project = Project() +fluent_files = project.get_fluent_files_by_dir(project.ru_locale_dir_path) + +######################################################################################################################## + +FluentFormatter.format(fluent_files) diff --git a/Tools/_sunrise/key.env b/Tools/_sunrise/key.env new file mode 100644 index 0000000000..9d796f7a31 --- /dev/null +++ b/Tools/_sunrise/key.env @@ -0,0 +1,2 @@ +lokalise_project_id=4706525766ab4ce5734b82.40263496 +lokalise_personal_token=cccdf4f3add308b8af995039f5c4846494a6df49 \ No newline at end of file diff --git a/Tools/_sunrise/keyfinder.py b/Tools/_sunrise/keyfinder.py new file mode 100644 index 0000000000..b06d428286 --- /dev/null +++ b/Tools/_sunrise/keyfinder.py @@ -0,0 +1,220 @@ +import typing +import logging + +from pydash import py_ + +from file import FluentFile +from fluentast import FluentAstAbstract +from fluentformatter import FluentFormatter +from project import Project +from fluent.syntax import ast, FluentParser, FluentSerializer + + +# Осуществляет актуализацию ключей. Находит файлы английского перевода, проверяет: есть ли русскоязычная пара +# Если нет - создаёт файл с копией переводов из англоязычного +# Далее, пофайлово проверяются ключи. Если в английском файле больше ключей - создает недостающие в русском, с английской копией перевода +# Отмечает русские файлы, в которых есть те ключи, что нет в аналогичных английских +# Отмечает русские файлы, у которых нет англоязычной пары + +######################################### Class defifitions ############################################################ +class RelativeFile: + def __init__(self, file: FluentFile, locale: typing.AnyStr, relative_path_from_locale: typing.AnyStr): + self.file = file + self.locale = locale + self.relative_path_from_locale = relative_path_from_locale + + +class FilesFinder: + def __init__(self, project: Project): + self.project: Project = project + self.created_files: typing.List[FluentFile] = [] + + def get_relative_path_dict(self, file: FluentFile, locale): + if locale == 'ru-RU': + return RelativeFile(file=file, locale=locale, + relative_path_from_locale=file.get_relative_path(self.project.ru_locale_dir_path)) + elif locale == 'en-US': + return RelativeFile(file=file, locale=locale, + relative_path_from_locale=file.get_relative_path(self.project.en_locale_dir_path)) + else: + raise Exception(f'Локаль {locale} не поддерживается') + + def get_file_pair(self, en_file: FluentFile) -> typing.Tuple[FluentFile, FluentFile]: + ru_file_path = en_file.full_path.replace('en-US', 'ru-RU') + ru_file = FluentFile(ru_file_path) + + return en_file, ru_file + + def execute(self): + self.created_files = [] + groups = self.get_files_pars() + keys_without_pair = list(filter(lambda g: len(groups[g]) < 2, groups)) + + for key_without_pair in keys_without_pair: + relative_file: RelativeFile = groups.get(key_without_pair)[0] + + if relative_file.locale == 'en-US': + ru_file = self.create_ru_analog(relative_file) + self.created_files.append(ru_file) + elif relative_file.locale == 'ru-RU': + is_engine_files = "robust-toolbox" in (relative_file.file.full_path) + is_corvax_files = "corvax" in (relative_file.file.full_path) + if not is_engine_files and not is_corvax_files: + self.warn_en_analog_not_exist(relative_file) + else: + raise Exception(f'Файл {relative_file.file.full_path} имеет неизвестную локаль "{relative_file.locale}"') + + return self.created_files + + def get_files_pars(self): + en_fluent_files = self.project.get_fluent_files_by_dir(project.en_locale_dir_path) + ru_fluent_files = self.project.get_fluent_files_by_dir(project.ru_locale_dir_path) + + en_fluent_relative_files = list(map(lambda f: self.get_relative_path_dict(f, 'en-US'), en_fluent_files)) + ru_fluent_relative_files = list(map(lambda f: self.get_relative_path_dict(f, 'ru-RU'), ru_fluent_files)) + relative_files = py_.flatten_depth(py_.concat(en_fluent_relative_files, ru_fluent_relative_files), depth=1) + + return py_.group_by(relative_files, 'relative_path_from_locale') + + def create_ru_analog(self, en_relative_file: RelativeFile) -> FluentFile: + en_file: FluentFile = en_relative_file.file + en_file_data = en_file.read_data() + ru_file_path = en_file.full_path.replace('en-US', 'ru-RU') + ru_file = FluentFile(ru_file_path) + ru_file.save_data(en_file_data) + + logging.info(f'Создан файл {ru_file_path} с переводами из английского файла') + + return ru_file + + def warn_en_analog_not_exist(self, ru_relative_file: RelativeFile): + file: FluentFile = ru_relative_file.file + en_file_path = file.full_path.replace('ru-RU', 'en-US') + + logging.warning(f'Файл {file.full_path} не имеет английского аналога по пути {en_file_path}') + + +class KeyFinder: + def __init__(self, files_dict): + self.files_dict = files_dict + self.changed_files: typing.List[FluentFile] = [] + + def execute(self) -> typing.List[FluentFile]: + self.changed_files = [] + for pair in self.files_dict: + ru_relative_file = py_.find(self.files_dict[pair], {'locale': 'ru-RU'}) + en_relative_file = py_.find(self.files_dict[pair], {'locale': 'en-US'}) + + if not en_relative_file or not ru_relative_file: + continue + + ru_file: FluentFile = ru_relative_file.file + en_file: FluentFile = en_relative_file.file + + self.compare_files(en_file, ru_file) + + return self.changed_files + + + def compare_files(self, en_file, ru_file): + ru_file_parsed: ast.Resource = ru_file.parse_data(ru_file.read_data()) + en_file_parsed: ast.Resource = en_file.parse_data(en_file.read_data()) + + self.write_to_ru_files(ru_file, ru_file_parsed, en_file_parsed) + self.log_not_exist_en_files(en_file, ru_file_parsed, en_file_parsed) + + + def write_to_ru_files(self, ru_file, ru_file_parsed, en_file_parsed): + for idx, en_message in enumerate(en_file_parsed.body): + if isinstance(en_message, ast.ResourceComment) or isinstance(en_message, ast.GroupComment) or isinstance(en_message, ast.Comment): + continue + + ru_message_analog_idx = py_.find_index(ru_file_parsed.body, lambda ru_message: self.find_duplicate_message_id_name(ru_message, en_message)) + have_changes = False + + # Attributes + if getattr(en_message, 'attributes', None) and ru_message_analog_idx != -1: + if not ru_file_parsed.body[ru_message_analog_idx].attributes: + ru_file_parsed.body[ru_message_analog_idx].attributes = en_message.attributes + have_changes = True + else: + for en_attr in en_message.attributes: + ru_attr_analog = py_.find(ru_file_parsed.body[ru_message_analog_idx].attributes, lambda ru_attr: ru_attr.id.name == en_attr.id.name) + if not ru_attr_analog: + ru_file_parsed.body[ru_message_analog_idx].attributes.append(en_attr) + have_changes = True + + # New elements + if ru_message_analog_idx == -1: + ru_file_body = ru_file_parsed.body + if (len(ru_file_body) >= idx + 1): + ru_file_parsed = self.append_message(ru_file_parsed, en_message, idx) + else: + ru_file_parsed = self.push_message(ru_file_parsed, en_message) + have_changes = True + + if have_changes: + serialized = serializer.serialize(ru_file_parsed) + self.save_and_log_file(ru_file, serialized, en_message) + + def log_not_exist_en_files(self, en_file, ru_file_parsed, en_file_parsed): + for idx, ru_message in enumerate(ru_file_parsed.body): + if isinstance(ru_message, ast.ResourceComment) or isinstance(ru_message, ast.GroupComment) or isinstance(ru_message, ast.Comment): + continue + + en_message_analog = py_.find(en_file_parsed.body, lambda en_message: self.find_duplicate_message_id_name(ru_message, en_message)) + + if not en_message_analog: + logging.warning(f'Ключ "{FluentAstAbstract.get_id_name(ru_message)}" не имеет английского аналога по пути {en_file.full_path}"') + + def append_message(self, ru_file_parsed, en_message, en_message_idx): + ru_message_part_1 = ru_file_parsed.body[0:en_message_idx] + ru_message_part_middle = [en_message] + ru_message_part_2 = ru_file_parsed.body[en_message_idx:] + new_body = py_.flatten_depth([ru_message_part_1, ru_message_part_middle, ru_message_part_2], depth=1) + ru_file_parsed.body = new_body + + return ru_file_parsed + + def push_message(self, ru_file_parsed, en_message): + ru_file_parsed.body.append(en_message) + return ru_file_parsed + + def save_and_log_file(self, file, file_data, message): + file.save_data(file_data) + logging.info(f'В файл {file.full_path} добавлен ключ "{FluentAstAbstract.get_id_name(message)}"') + self.changed_files.append(file) + + def find_duplicate_message_id_name(self, ru_message, en_message): + ru_element_id_name = FluentAstAbstract.get_id_name(ru_message) + en_element_id_name = FluentAstAbstract.get_id_name(en_message) + + if not ru_element_id_name or not en_element_id_name: + return False + + if ru_element_id_name == en_element_id_name: + return ru_message + else: + return None + +######################################## Var definitions ############################################################### + +logging.basicConfig(level = logging.INFO) +project = Project() +parser = FluentParser() +serializer = FluentSerializer(with_junk=True) +files_finder = FilesFinder(project) +key_finder = KeyFinder(files_finder.get_files_pars()) + +######################################################################################################################## + +print('Проверка актуальности файлов ...') +created_files = files_finder.execute() +if len(created_files): + print('Форматирование созданных файлов ...') + FluentFormatter.format(created_files) +print('Проверка актуальности ключей ...') +changed_files = key_finder.execute() +if len(changed_files): + print('Форматирование изменённых файлов ...') + FluentFormatter.format(changed_files) diff --git a/Tools/_sunrise/lokalise_fluent_ast_comparer_manager.py b/Tools/_sunrise/lokalise_fluent_ast_comparer_manager.py new file mode 100644 index 0000000000..cdbc84d5bd --- /dev/null +++ b/Tools/_sunrise/lokalise_fluent_ast_comparer_manager.py @@ -0,0 +1,48 @@ +from fluent.syntax import ast + +from fluentast import FluentAstMessage +from fluentastcomparer import FluentAstComparer +from fluentastmanager import FluentAstManager + + +class LokaliseFluentAstComparerManager: + def __init__(self, sourse_parsed: ast.Resource, target_parsed: ast.Resource): + self.sourse_parsed = sourse_parsed + self.target_parsed = target_parsed + self.comparer = FluentAstComparer(sourse_parsed, target_parsed) + self.ast_manager = FluentAstManager(sourse_parsed, target_parsed) + + def for_update(self): + for_update = self.comparer.get_not_equal_exist_values_with_attrs() + + if not len(for_update): + return [] + + return for_update + + def update(self, for_update): + for update in for_update: + idx = self.comparer.sourse_parsed.body.index(update.element) + update_mess: FluentAstMessage = self.comparer.find_message_by_id_name(update.get_id_name(), + self.comparer.target_elements) + self.ast_manager.update_by_index(idx, update_mess.element) + + return self.ast_manager.sourse_parsed + + def for_delete(self): + for_delete = self.comparer.get_not_exist_id_names() + + if len(for_delete): + keys = list(map(lambda el: el.get_id_name(), for_delete)) + print(f'Следующие ключи есть в lokalise, но нет в файле. Возможно, их нужно удалить из lokalise: {keys}') + + return for_delete + + def for_create(self): + for_create = self.comparer.get_not_equal_id_names() + + if len(for_create): + keys = list(map(lambda el: el.get_id_name(), for_create)) + print(f'Следующих ключей файла нет в lokalise. Необходимо добавить: {keys}') + + return for_create diff --git a/Tools/_sunrise/lokalise_project.py b/Tools/_sunrise/lokalise_project.py new file mode 100644 index 0000000000..861f317942 --- /dev/null +++ b/Tools/_sunrise/lokalise_project.py @@ -0,0 +1,33 @@ +import lokalise +import typing +from lokalisemodels import LokaliseKey +from pydash import py_ + +class LokaliseProject: + def __init__(self, project_id, personal_token): + self.project_id = "4706525766ab4ce5734b82.40263496" + self.personal_token = "cccdf4f3add308b8af995039f5c4846494a6df49" + self.client = lokalise.Client(self.personal_token) + + def get_all_keys(self) -> typing.List[LokaliseKey]: + page = 1 + keys = self.get_keys(page=page) + keys_items: typing.List[lokalise.client.KeyModel] = [] + general_count = 0 + + while (general_count < keys.total_count): + general_count = general_count + len(keys.items) + keys_items = py_.flatten_depth(py_.concat(keys_items, keys.items), depth=1) + + if (general_count == keys.total_count): + break + + next_page = page = page + 1 + keys = self.get_keys(page=next_page) + + sorted_list = py_.sort(keys_items, key=lambda item: item.translations_modified_at_timestamp, reverse=True) + + return list(map(lambda k: LokaliseKey(k), sorted_list)) + + def get_keys(self, page): + return self.client.keys(self.project_id, {'page': page, 'limit': 5000, 'include_translations': 1}) diff --git a/Tools/_sunrise/lokalisemodels.py b/Tools/_sunrise/lokalisemodels.py new file mode 100644 index 0000000000..98e238cc18 --- /dev/null +++ b/Tools/_sunrise/lokalisemodels.py @@ -0,0 +1,64 @@ +import typing +import os +from pydash import py_ +from project import Project + +class LocalePath: + def __init__(self, relative_file_path): + self.ru = os.path.join(Project().ru_locale_dir_path, relative_file_path) + self.en = os.path.join(Project().en_locale_dir_path, relative_file_path) + + +class LokaliseTranslation: + def __init__(self, data, key_name: typing.AnyStr): + self.key_name = key_name, + self.data = data + +class LokaliseKey: + def __init__(self, data): + self.data = data + self.key_name = self.data.key_name['web'] + self.key_base_name = self.get_key_base_name(self.key_name) + self.is_attr = self.check_is_attr() + + def get_file_path(self): + + relative_dir_path = '{relative_file_path}.ftl'.format( + relative_file_path='/'.join(self.data.key_name['web'].split('.')[0].split('::'))) + + return LocalePath(relative_dir_path) + + def get_key_base_name(self, key_name): + splitted_name = key_name.split('.') + return splitted_name[0] + + def get_key_last_name(self, key_name): + splitted_name = key_name.split('.') + return py_.last(splitted_name) + + def get_parent_key(self): + if self.is_attr: + splitted_name = self.key_name.split('.')[0:-1] + return '.'.join(splitted_name) + else: + return None + + def check_is_attr(self): + return len(self.key_name.split('.')) > 2 + + def serialize(self): + if self.is_attr: + return self.serialize_attr() + else: + return self.serialize_message() + + + + def serialize_attr(self): + return '.{name} = {value}'.format(name=self.get_key_last_name(self.key_name), value=self.get_translation('ru').data['translation']) + + def serialize_message(self): + return '{name} = {value}'.format(name=self.get_key_last_name(self.key_name), value=self.get_translation('ru').data['translation']) + + def get_translation(self, language_iso='ru'): + return list(map(lambda data: LokaliseTranslation(key_name=self.data.key_name['web'], data=data), py_.filter(self.data.translations, {'language_iso': language_iso})))[0] diff --git a/Tools/_sunrise/project.py b/Tools/_sunrise/project.py new file mode 100644 index 0000000000..7fcc9c7c32 --- /dev/null +++ b/Tools/_sunrise/project.py @@ -0,0 +1,31 @@ +import pathlib +import os +import glob +from file import FluentFile + +class Project: + def __init__(self): + self.base_dir_path = pathlib.Path(os.path.abspath(os.curdir)).parent.parent.resolve() + self.resources_dir_path = os.path.join(self.base_dir_path, 'Resources') + self.locales_dir_path = os.path.join(self.resources_dir_path, 'Locale') + self.ru_locale_dir_path = os.path.join(self.locales_dir_path, 'ru-RU') + self.en_locale_dir_path = os.path.join(self.locales_dir_path, 'en-US') + self.prototypes_dir_path = os.path.join(self.resources_dir_path, "Prototypes") + self.en_locale_prototypes_dir_path = os.path.join(self.en_locale_dir_path, 'ss14-ru', 'prototypes') + self.ru_locale_prototypes_dir_path = os.path.join(self.ru_locale_dir_path, 'ss14-ru', 'prototypes') + + def get_files_paths_by_dir(self, dir_path, files_extenstion): + return glob.glob(f'{dir_path}/**/*.{files_extenstion}', recursive=True) + + def get_fluent_files_by_dir(self, dir_path): + files = [] + files_paths_list = glob.glob(f'{dir_path}/**/*.ftl', recursive=True) + + for file_path in files_paths_list: + try: + files.append(FluentFile(file_path)) + except: + continue + + return files + diff --git a/Tools/_sunrise/requirements.txt b/Tools/_sunrise/requirements.txt new file mode 100644 index 0000000000..c68858f018 Binary files /dev/null and b/Tools/_sunrise/requirements.txt differ diff --git a/Tools/_sunrise/translationassembler.py b/Tools/_sunrise/translationassembler.py new file mode 100644 index 0000000000..4ce893626f --- /dev/null +++ b/Tools/_sunrise/translationassembler.py @@ -0,0 +1,67 @@ +import logging +import typing + +from fluent.syntax import FluentParser, FluentSerializer +from pydash import py_ + +from file import FluentFile +from fluentast import FluentSerializedMessage +from lokalise_fluent_ast_comparer_manager import LokaliseFluentAstComparerManager +from lokalise_project import LokaliseProject +from lokalisemodels import LokaliseKey +import os + +######################################### Class defifitions ############################################################ + +# TODO непереведенные элементы приходят как { "" }. Необходимо сохранять английский перевод +class TranslationsAssembler: + def __init__(self, items: typing.List[LokaliseKey]): + self.group = py_.group_by(items, 'key_base_name') + keys = list(self.group.keys()) + self.sorted_keys = py_.sort_by(keys, lambda key: self.sort_by_translations_timestamp(self.group[key]), + reverse=True) + + def execute(self): + for keys in self.group: + full_message = FluentSerializedMessage.from_lokalise_keys(self.group[keys]) + parsed_message = FluentParser().parse(full_message) + ru_full_path = self.group[keys][0].get_file_path().ru + ru_file = FluentFile(ru_full_path) + try: + ru_file_parsed = ru_file.read_parsed_data() + except: + logging.error(f'Файла {ru_file.full_path} не существует') + continue + + manager = LokaliseFluentAstComparerManager(sourse_parsed=ru_file_parsed, target_parsed=parsed_message) + + for_update = manager.for_update() + for_create = manager.for_create() + for_delete = manager.for_delete() + + if len(for_update): + updated_ru_file_parsed = manager.update(for_update) + updated_ru_file_serialized = FluentSerializer(with_junk=True).serialize(updated_ru_file_parsed) + ru_file.save_data(updated_ru_file_serialized) + + updated_keys = list(map(lambda el: el.get_id_name(), for_update)) + logging.info(f'Обновлены ключи: {updated_keys} в файле {ru_file.full_path}') + + def sort_by_translations_timestamp(self, list): + sorted_list = py_.sort_by(list, 'data.translations_modified_at_timestamp', reverse=True) + + return sorted_list[0].data.translations_modified_at_timestamp + +######################################## Var definitions ############################################################### + +logging.basicConfig(level=logging.INFO) +lokalise_project_id = os.getenv('lokalise_project_id') +lokalise_personal_token = os.getenv('lokalise_personal_token') +lokalise_project = LokaliseProject(project_id=lokalise_project_id, + personal_token=lokalise_personal_token) +all_keys: typing.List[LokaliseKey] = lokalise_project.get_all_keys() +translations_assembler = TranslationsAssembler(all_keys) + +######################################################################################################################## + +translations_assembler.execute() diff --git a/Tools/_sunrise/yamlextractor.py b/Tools/_sunrise/yamlextractor.py new file mode 100644 index 0000000000..41fff75403 --- /dev/null +++ b/Tools/_sunrise/yamlextractor.py @@ -0,0 +1,107 @@ +import os + +from fluent.syntax.parser import FluentParser +from fluent.syntax.serializer import FluentSerializer + +from file import YAMLFile, FluentFile +from fluentast import FluentSerializedMessage, FluentAstAttributeFactory +from fluentformatter import FluentFormatter +from project import Project +import logging + +######################################### Class defifitions ############################################################ +class YAMLExtractor: + def __init__(self, yaml_files): + self.yaml_files = yaml_files + + def execute(self): + for yaml_file in yaml_files: + yaml_elements = yaml_file.get_elements(yaml_file.parse_data(yaml_file.read_data())) + + if not len(yaml_elements): + continue + + fluent_file_serialized = self.get_serialized_fluent_from_yaml_elements(yaml_elements) + + if not fluent_file_serialized: + continue + + pretty_fluent_file_serialized = formatter.format_serialized_file_data(fluent_file_serialized) + + relative_parent_dir = yaml_file.get_relative_parent_dir(project.prototypes_dir_path).lower() + file_name = yaml_file.get_name() + + en_fluent_file_path = self.create_en_fluent_file(relative_parent_dir, file_name, pretty_fluent_file_serialized) + ru_fluent_file_path = self.create_ru_fluent_file(en_fluent_file_path) + + def get_serialized_fluent_from_yaml_elements(self, yaml_elements): + fluent_serialized_messages = list( + map(lambda el: FluentSerializedMessage.from_yaml_element(el.id, el.name, FluentAstAttributeFactory.from_yaml_element(el), el.parent_id), yaml_elements) + ) + fluent_exist_serialized_messages = list(filter(lambda m: m, fluent_serialized_messages)) + + if not len(fluent_exist_serialized_messages): + return None + + return '\n'.join(fluent_exist_serialized_messages) + + def create_en_fluent_file(self, relative_parent_dir, file_name, file_data): + # Проверяем, если информация берется из директории _Sunrise + if '_sunrise' in relative_parent_dir: + # Создаем путь для сохранения в en-US/_sunrise/_prototypes + en_new_dir_path = os.path.join(project.en_locale_dir_path, '_sunrise', '_prototypes', relative_parent_dir.replace('_sunrise', '').strip(os.sep)) + else: + # Обычный путь сохранения + en_new_dir_path = os.path.join(project.en_locale_prototypes_dir_path, relative_parent_dir) + + # Убедимся, что директория существует + os.makedirs(en_new_dir_path, exist_ok=True) + + # Формируем полный путь к файлу + en_fluent_file_path = os.path.join(en_new_dir_path, f'{file_name}.ftl') + + # Сохраняем файл + en_fluent_file = FluentFile(en_fluent_file_path) + en_fluent_file.save_data(file_data) + + logging.info(f'Актуализирован файл английской локализации {en_fluent_file.full_path}') + + return en_fluent_file.full_path + + def create_ru_fluent_file(self, en_analog_file_path): + if '_sunrise' in en_analog_file_path.lower(): + # Если уже есть _sunrise, просто заменяем en-US на ru-RU + ru_file_full_path = en_analog_file_path.replace('en-US', 'ru-RU') + else: + # Обычный путь сохранения + ru_file_full_path = en_analog_file_path.replace('en-US', 'ru-RU') + + # Убедимся, что директория существует + os.makedirs(os.path.dirname(ru_file_full_path), exist_ok=True) + + if os.path.isfile(ru_file_full_path): + return + else: + en_file = FluentFile(f'{en_analog_file_path}') + file = FluentFile(f'{ru_file_full_path}') + file.save_data(en_file.read_data()) + logging.info(f'Создан файл русской локализации {ru_file_full_path}') + + return ru_file_full_path + + +######################################## Var definitions ############################################################### + +logging.basicConfig(level = logging.INFO) +project = Project() +serializer = FluentSerializer() +parser = FluentParser() +formatter = FluentFormatter() + +yaml_files_paths = project.get_files_paths_by_dir(project.prototypes_dir_path, 'yml') +yaml_files = list(map(lambda yaml_file_path: YAMLFile(yaml_file_path), yaml_files_paths)) + +######################################################################################################################## + +logging.info(f'Поиск yaml-файлов ...') +YAMLExtractor(yaml_files).execute() diff --git a/Tools/_sunrise/yamlmodels.py b/Tools/_sunrise/yamlmodels.py new file mode 100644 index 0000000000..0c35e9af71 --- /dev/null +++ b/Tools/_sunrise/yamlmodels.py @@ -0,0 +1,26 @@ +class YAMLEntity: + def __init__(self, id, name, description, suffix, parent_id = None): + self.id = id + self.name = name + self.description = description + self.suffix = suffix + self.parent_id = parent_id + + +class YAMLElements: + def __init__(self, items): + self.elements = list(map(lambda i: self.create_element(i), items)) + + def create_element(self, item): + if not 'id' in item: + return None + + if item['type'] == 'entity': + entity = YAMLEntity(item['id'], item['name'] if 'name' in item else None, + item['description'] if 'description' in item else None, + item['suffix'] if 'suffix' in item else None, + item['parent'] if 'parent' in item else None + ) + return entity + else: + return None