translations fix

Signed-off-by: galeon <dertiellis@gmail.com>
This commit is contained in:
galeon committed 2026-10-09 12:29:21 +03:00
1 parent fd14b54d8a
commit 0f370167db
9 files changed
+18 -134

No files matched your search

-3
View File
@@ -1,12 +1,9 @@
import QtQuick import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
// Комбобокс в общем тёмном стиле окна.
ComboBox { ComboBox {
id: darkCombo id: darkCombo
// Та же стрелка, что у комбобоксов профиля и версии на главном окне,
// вместо двойного шеврона Basic-стиля.
indicator: Image { indicator: Image {
width: 10; height: 10 width: 10; height: 10
x: darkCombo.width - width - 12 x: darkCombo.width - width - 12
+1 -20
View File
@@ -4,22 +4,16 @@ import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 2.15 import QtQuick.Layouts 2.15
// Выбор сборки Java отдельным окном: слева типы, справа сами версии с поиском.
// Устроено так же, как выбор версии Minecraft, но выбранное здесь ещё и
// качается — в отличие от версий игры, где загрузку начинает сама сборка.
Dialog { Dialog {
id: picker id: picker
required property var backend required property var backend
// Сборка, с которой окно открылось: по «Отмене» выбор к ней и возвращается.
property string selectedId: "" property string selectedId: ""
property string category: "java" property string category: "java"
property string filterText: "" property string filterText: ""
property bool installedOnly: false property bool installedOnly: false
// Java, ниже которой выбранной версии игры не запуститься. 0 — версия игры
// не выбрана, предупреждать не о чем.
property int requiredMajor: 0 property int requiredMajor: 0
signal runtimeChosen(string runtimeId) signal runtimeChosen(string runtimeId)
@@ -43,8 +37,6 @@ Dialog {
{ key: "jre", title: Loc.t.common.java.kind.jre, hint: Loc.t.java.filter.temurinJre } { key: "jre", title: Loc.t.common.java.kind.jre, hint: Loc.t.java.filter.temurinJre }
] ]
// Каталог приходит из C++ уже отсортированным (новые сверху, скачанные
// первыми), поэтому здесь только отбор — порядок не трогаем.
readonly property var visibleEntries: { readonly property var visibleEntries: {
const query = picker.filterText.trim().toLowerCase() const query = picker.filterText.trim().toLowerCase()
const source = picker.backend.javaCatalog const source = picker.backend.javaCatalog
@@ -107,7 +99,6 @@ Dialog {
runtimeList.positionViewAtIndex(index, ListView.Center) runtimeList.positionViewAtIndex(index, ListView.Center)
} }
// «Выбрать» и скачивает: сборка, которой нет на диске, запуску не поможет.
function acceptSelection() { function acceptSelection() {
const entry = picker.selectedEntry const entry = picker.selectedEntry
if (!entry) if (!entry)
@@ -138,7 +129,6 @@ Dialog {
contentItem: RowLayout { contentItem: RowLayout {
spacing: 0 spacing: 0
// ── Типы сборок ─────────────────────────────────────────────────
Rectangle { Rectangle {
Layout.preferredWidth: 170 Layout.preferredWidth: 170
Layout.fillHeight: true Layout.fillHeight: true
@@ -195,8 +185,6 @@ Dialog {
Item { width: 1; height: 8 } Item { width: 1; height: 8 }
// Требование выбранной сборки — чтобы не пришлось держать в
// голове, какая Java нужна какой версии игры.
Text { Text {
width: parent.width - 4 width: parent.width - 4
visible: picker.requiredMajor > 0 visible: picker.requiredMajor > 0
@@ -215,7 +203,6 @@ Dialog {
color: "#333333" color: "#333333"
} }
// ── Версии ──────────────────────────────────────────────────────
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
@@ -317,8 +304,7 @@ Dialog {
required property int index required property int index
readonly property bool chosen: picker.selectedId === runtimeRow.modelData.id readonly property bool chosen: picker.selectedId === runtimeRow.modelData.id
// Сборка ниже требования игры выбирается, но помечается: она
// может пригодиться для другой сборки.
readonly property bool tooOld: picker.requiredMajor > 0 readonly property bool tooOld: picker.requiredMajor > 0
&& runtimeRow.modelData.major < picker.requiredMajor && runtimeRow.modelData.major < picker.requiredMajor
@@ -353,8 +339,6 @@ Dialog {
} }
Text { Text {
// Колонка растянута якорями, поэтому её ширина
// задана, а не выведена из детей — цикла тут нет.
width: parent.width width: parent.width
elide: Text.ElideRight elide: Text.ElideRight
color: runtimeRow.chosen ? "#1e1e1e" color: runtimeRow.chosen ? "#1e1e1e"
@@ -388,8 +372,6 @@ Dialog {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
// Сборки весят по двести мегабайт — удалять их нужно
// прямо здесь, иначе папка лаунчера растёт молча.
Image { Image {
visible: runtimeRow.modelData.installed visible: runtimeRow.modelData.installed
source: "images/Trash.svg" source: "images/Trash.svg"
@@ -430,7 +412,6 @@ Dialog {
} }
} }
// Пустая категория, ничего не нашлось или каталог ещё не пришёл.
Text { Text {
anchors.centerIn: runtimeList anchors.centerIn: runtimeList
width: runtimeList.width - 20 width: runtimeList.width - 20
-2
View File
@@ -1,7 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
// Подпись + поле ввода одной колонкой — используется в настройках.
Column { Column {
id: labelled id: labelled
@@ -10,7 +9,6 @@ Column {
property string label: "" property string label: ""
property string placeholder: "" property string placeholder: ""
// Правка закончена: поле потеряло фокус или пользователь нажал Enter.
signal editingFinished() signal editingFinished()
spacing: 3 spacing: 3
-28
View File
@@ -1,13 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
// Один модлоадер в карточке сборки: чекбокс и, когда он отмечен, список версий
// именно под выбранную версию Minecraft.
//
// Совместимость проверять не нужно: backend.loaderVersions() отдаёт список уже
// под конкретную версию игры, поэтому несовместимой строки в нём не бывает.
// Пустой список — это «лоадер не поддерживает эту версию игры», и он тоже
// показывается словами, а не пустым выпадающим списком.
Column { Column {
id: loaderRow id: loaderRow
@@ -15,21 +8,13 @@ Column {
required property string loaderKey required property string loaderKey
required property string title required property string title
// Версия Minecraft, выбранная в карточке. Её смена сбрасывает выбор.
property string gameVersion: "" property string gameVersion: ""
property alias checked: box.checked property alias checked: box.checked
property string selectedVersion: "" property string selectedVersion: ""
property var entries: [] property var entries: []
// Пользователь щёлкнул чекбокс — карточка снимет отметки с остальных
// лоадеров: одновременно в .minecraft может жить только один.
signal userChecked() signal userChecked()
// Отметка или версия изменились — карточке пора сохранить сборку.
signal changed() signal changed()
// Ставится, пока карточка заполняет строку данными сборки: в это время
// обработчики не должны считать изменения пользовательскими.
property bool applying: false property bool applying: false
spacing: 4 spacing: 4
@@ -42,16 +27,9 @@ Column {
return -1 return -1
} }
// Единственное место, где меняются entries и selectedVersion. Через него
// проходят все три пути — кэш, ответ сети и заполнение из сборки, — потому
// что выбранная версия обязана существовать в списке под текущую версию
// игры. Раньше проверял только путь из сети, и версия лоадера от прошлой
// версии игры доживала до сохранения сборки.
function applyEntries(list) { function applyEntries(list) {
loaderRow.entries = list loaderRow.entries = list
// Список отсортирован новыми вперёд, поэтому максимально доступная
// версия для этой версии игры — всегда первая строка.
if (loaderRow.versionIndex(loaderRow.selectedVersion) < 0) { if (loaderRow.versionIndex(loaderRow.selectedVersion) < 0) {
const fallback = list.length > 0 ? list[0].version : "" const fallback = list.length > 0 ? list[0].version : ""
const changedValue = loaderRow.selectedVersion !== fallback const changedValue = loaderRow.selectedVersion !== fallback
@@ -68,26 +46,20 @@ Column {
loaderRow.applyEntries([]) loaderRow.applyEntries([])
return return
} }
// Сначала из кэша — список появляется мгновенно, — потом обновление сети.
loaderRow.applyEntries(loaderRow.backend.loaderVersions(loaderRow.loaderKey, loaderRow.applyEntries(loaderRow.backend.loaderVersions(loaderRow.loaderKey,
loaderRow.gameVersion)) loaderRow.gameVersion))
loaderRow.backend.refreshLoaderVersions(loaderRow.loaderKey, loaderRow.gameVersion) loaderRow.backend.refreshLoaderVersions(loaderRow.loaderKey, loaderRow.gameVersion)
} }
// Заполнение из сохранённой сборки: без сигналов changed().
function applyBuild(loader, loaderVersion) { function applyBuild(loader, loaderVersion) {
loaderRow.applying = true loaderRow.applying = true
box.checked = loader === loaderRow.loaderKey box.checked = loader === loaderRow.loaderKey
// Версию из сборки берём как пожелание: если под выбранную версию игры
// её нет, applyEntries() заменит её на максимально доступную.
loaderRow.selectedVersion = box.checked ? loaderVersion : "" loaderRow.selectedVersion = box.checked ? loaderVersion : ""
loaderRow.reload() loaderRow.reload()
loaderRow.applying = false loaderRow.applying = false
} }
onGameVersionChanged: { onGameVersionChanged: {
// Сборка лоадера привязана к версии игры: под новой версией прежний
// выбор недействителен, и список перезапрашивается заново.
loaderRow.selectedVersion = "" loaderRow.selectedVersion = ""
loaderRow.reload() loaderRow.reload()
} }
-12
View File
@@ -4,19 +4,12 @@ import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtWebEngine import QtWebEngine
// Вход в аккаунт Microsoft — то же самое, что делает официальный лаунчер:
// страница входа показывается внутри приложения, а лаунчер ждёт, когда браузер
// уйдёт на redirect_uri с кодом авторизации в адресе.
Dialog { Dialog {
id: msLogin id: msLogin
required property var backend required property var backend
// Окно ничего не знает про тосты главного окна: о неудаче сообщает сигналом.
signal failed(string message) signal failed(string message)
// Код уже отдан backend — второй раз по тому же адресу не реагируем:
// WebEngineView успевает сообщить об urlChanged несколько раз.
property bool codeTaken: false property bool codeTaken: false
anchors.centerIn: parent anchors.centerIn: parent
@@ -32,8 +25,6 @@ Dialog {
open() open()
} }
// Разбор адреса живёт в C++: правила совпадения должны совпадать с теми,
// по которым сервис строит сам redirect_uri.
function handleUrl(url) { function handleUrl(url) {
if (codeTaken) if (codeTaken)
return return
@@ -76,9 +67,6 @@ Dialog {
} }
contentItem: Item { contentItem: Item {
// Профиль без storageName — значит без диска: куки живут только пока
// работает лаунчер и в общий браузер не попадают. За выбор аккаунта в
// пределах сессии отвечает prompt=select_account в адресе входа.
WebEngineProfilePrototype { WebEngineProfilePrototype {
id: msProfile id: msProfile
} }
-4
View File
@@ -1,16 +1,12 @@
import QtQuick import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
// Плашка хода долгой операции в левом нижнем углу: не задевает кнопку запуска,
// сообщение по центру и кнопки папки с настройками.
Rectangle { Rectangle {
id: panel id: panel
property string title: "" property string title: ""
property string status: "" property string status: ""
// -1 — итог ещё неизвестен, показываем «…» вместо процентов.
property double fraction: -1 property double fraction: -1
// Необязательная вторая строка: мегабайты у загрузки, путь у архивации.
property string detail: "" property string detail: ""
property bool cancellable: true property bool cancellable: true
-26
View File
@@ -3,23 +3,11 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
// Готовые сезонные сборки с сервера лаунчера: таблица со всем, что нужно
// решить, ставить её или нет, и одна кнопка, которая делает всё остальное —
// заводит сборку, ставит версию игры, модлоадер, Java и раскладывает файлы.
//
// Таблица собрана из Row фиксированных колонок, а не из TableView: в проекте
// нет ни одной модели QAbstractItemModel, а строки приходят готовыми
// QVariantMap — заводить ради семи колонок отдельную модель незачем.
Dialog { Dialog {
id: seasons id: seasons
required property var backend required property var backend
// Выбранная строка. Хранится по id, а не по индексу: список обновляется
// под руками, и индекс после обновления указывал бы на другую сборку.
property string selectedId: "" property string selectedId: ""
// Ширины колонок в одном месте: их повторяют и шапка, и делегат.
readonly property var columns: [ readonly property var columns: [
{ key: "name", title: Loc.t.common.header.name, width: 250, align: Text.AlignLeft }, { key: "name", title: Loc.t.common.header.name, width: 250, align: Text.AlignLeft },
{ key: "minecraftVersion", title: Loc.t.common.header.version, width: 90, align: Text.AlignLeft }, { key: "minecraftVersion", title: Loc.t.common.header.version, width: 90, align: Text.AlignLeft },
@@ -34,8 +22,6 @@ Dialog {
padding: 0 padding: 0
width: 960 width: 960
height: 560 height: 560
// Пока идёт установка, окно закрывается только кнопкой: случайный щелчок
// мимо не должен спрятать единственную видимую отмену.
closePolicy: seasons.backend.seasonalInstalling closePolicy: seasons.backend.seasonalInstalling
? Popup.NoAutoClose ? Popup.NoAutoClose
: (Popup.CloseOnEscape | Popup.CloseOnPressOutside) : (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
@@ -47,8 +33,6 @@ Dialog {
border.width: 1 border.width: 1
} }
// Строки приходят из C++ уже отсортированными и сведёнными с локальными
// записями — здесь только показ.
readonly property var entries: seasons.backend.seasonalCatalog readonly property var entries: seasons.backend.seasonalCatalog
readonly property var selectedEntry: { readonly property var selectedEntry: {
@@ -79,7 +63,6 @@ Dialog {
return String(entry[key]) return String(entry[key])
} }
// Свежий кэш отвечает без сети, поэтому дёргаем при каждом открытии.
onAboutToShow: seasons.backend.refreshSeasonalCatalog(false) onAboutToShow: seasons.backend.refreshSeasonalCatalog(false)
header: Item { header: Item {
@@ -101,7 +84,6 @@ Dialog {
contentItem: Item { contentItem: Item {
// ── Шапка таблицы ───────────────────────────────────────────────
Row { Row {
id: tableHeader id: tableHeader
anchors.top: parent.top anchors.top: parent.top
@@ -139,9 +121,6 @@ Dialog {
color: "#333333" color: "#333333"
} }
// ── Сообщение вместо таблицы ────────────────────────────────────
// Пустой список и ошибка выглядят одинаково пустыми, поэтому причина
// показывается прямо на месте строк.
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width - 80 width: parent.width - 80
@@ -157,7 +136,6 @@ Dialog {
: seasons.backend.seasonalCatalogError) : seasons.backend.seasonalCatalogError)
} }
// ── Строки ──────────────────────────────────────────────────────
ListView { ListView {
id: buildList id: buildList
anchors.top: headerLine.bottom anchors.top: headerLine.bottom
@@ -243,8 +221,6 @@ Dialog {
color: "#333333" color: "#333333"
} }
// Описание и размер выбранной сборки: они длинные и в таблицу не
// помещаются, а решение принимается по ним.
Column { Column {
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 18 anchors.leftMargin: 18
@@ -314,8 +290,6 @@ Dialog {
id: installButton id: installButton
width: 150 width: 150
height: 36 height: 36
// Установка занимает и панель загрузки, и .minecraft целиком:
// пока лаунчер занят чем угодно, вторую начинать нельзя.
enabled: seasons.selectedEntry !== null && !seasons.backend.busy enabled: seasons.selectedEntry !== null && !seasons.backend.busy
contentItem: Text { contentItem: Text {
text: seasons.selectedEntry && seasons.selectedEntry.updateAvailable text: seasons.selectedEntry && seasons.selectedEntry.updateAvailable
-22
View File
@@ -4,25 +4,16 @@ import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 2.15 import QtQuick.Layouts 2.15
// Выбор версии Minecraft отдельным окном: слева категории, справа сами версии
// с поиском сверху.
//
// Раньше это был выпадающий список на всю тысячу версий: найти в нём бету 1.7
// можно было только поиском по точному номеру. Категории делят список на
// обозримые части, а поиск работает внутри выбранной.
Dialog { Dialog {
id: picker id: picker
required property var backend required property var backend
// Версия, с которой окно открылось: по «Отмене» выбор к ней и возвращается.
property string selectedId: "" property string selectedId: ""
property string category: "release" property string category: "release"
property string filterText: "" property string filterText: ""
property bool installedOnly: false property bool installedOnly: false
// Выбранная версия подтверждена кнопкой или двойным щелчком. Имя не
// accepted(): такой сигнал у Dialog уже есть, и переопределить его нельзя.
signal versionChosen(string versionId) signal versionChosen(string versionId)
modal: true modal: true
@@ -46,8 +37,6 @@ Dialog {
{ key: "other", title: Loc.t.version.filter.other } { key: "other", title: Loc.t.version.filter.other }
] ]
// Каталог приходит из C++ уже отсортированным (новые сверху), поэтому здесь
// только отбор — порядок не трогаем.
readonly property var visibleEntries: { readonly property var visibleEntries: {
const query = picker.filterText.trim().toLowerCase() const query = picker.filterText.trim().toLowerCase()
const source = picker.backend.versionCatalog const source = picker.backend.versionCatalog
@@ -69,7 +58,6 @@ Dialog {
picker.selectedId = versionId picker.selectedId = versionId
picker.filterText = "" picker.filterText = ""
searchField.text = "" searchField.text = ""
// Открываемся на категории текущей версии, а не всегда на релизах.
picker.category = picker.categoryOf(versionId) picker.category = picker.categoryOf(versionId)
picker.backend.refreshVersionCatalog() picker.backend.refreshVersionCatalog()
picker.open() picker.open()
@@ -117,9 +105,6 @@ Dialog {
return false return false
} }
// Спрашиваем до удаления: версию придётся качать заново, а профили
// модлоадеров поверх неё — переустанавливать. Id запоминаем здесь:
// к моменту ответа строка под курсором может быть уже другой.
function askRemove(versionId) { function askRemove(versionId) {
const info = picker.backend.versionRemovalInfo(versionId) const info = picker.backend.versionRemovalInfo(versionId)
if (!info || info.installed !== true) if (!info || info.installed !== true)
@@ -133,8 +118,6 @@ Dialog {
function performRemove(versionId) { function performRemove(versionId) {
picker.backend.removeVersion(versionId) picker.backend.removeVersion(versionId)
// Профиль модлоадера был в каталоге только потому, что установлен:
// после удаления строка исчезает совсем, и выбор надо снять.
if (picker.selectedId === versionId && !picker.catalogHas(versionId)) if (picker.selectedId === versionId && !picker.catalogHas(versionId))
picker.selectedId = "" picker.selectedId = ""
picker.revealSelected() picker.revealSelected()
@@ -305,7 +288,6 @@ Dialog {
contentItem: RowLayout { contentItem: RowLayout {
spacing: 0 spacing: 0
// ── Категории ───────────────────────────────────────────────────
Rectangle { Rectangle {
Layout.preferredWidth: 150 Layout.preferredWidth: 150
Layout.fillHeight: true Layout.fillHeight: true
@@ -358,7 +340,6 @@ Dialog {
color: "#333333" color: "#333333"
} }
// ── Версии ──────────────────────────────────────────────────────
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
@@ -493,8 +474,6 @@ Dialog {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
// Версия весит от десятков мегабайт, а галочка рядом —
// единственное место, где видно, что именно скачано.
Image { Image {
visible: versionRow.modelData.installed visible: versionRow.modelData.installed
source: "images/Trash.svg" source: "images/Trash.svg"
@@ -531,7 +510,6 @@ Dialog {
} }
} }
// Пустая категория, ничего не нашлось или манифест ещё не пришёл.
Text { Text {
anchors.centerIn: versionList anchors.centerIn: versionList
width: versionList.width - 20 width: versionList.width - 20
+17 -17
View File
@@ -40,7 +40,7 @@
"en": "Open mods folder" "en": "Open mods folder"
}, },
"app.tooltip.seasonal": { "app.tooltip.seasonal": {
"ru": "Сезонные сборки от команды Мельника", "ru": "Сезонные сборки от команды Galeon'а",
"en": "Seasonal builds from Galeon team" "en": "Seasonal builds from Galeon team"
}, },
"app.tooltip.settings": { "app.tooltip.settings": {
@@ -156,8 +156,8 @@
"en": "The Microsoft account owns the game but has no game profile yet. Sign in to the official launcher once and pick a nickname." "en": "The Microsoft account owns the game but has no game profile yet. Sign in to the official launcher once and pick a nickname."
}, },
"auth.msa.error.noWebEngine": { "auth.msa.error.noWebEngine": {
"ru": "Эта сборка лаунчера собрана без Qt WebEngine — показать окно входа Microsoft нечем", "ru": "Эта сборка лаунчера собрана без Qt WebEngine",
"en": "This launcher build was made without Qt WebEngine — there is nothing to show the Microsoft sign-in window with" "en": "This launcher build was made without Qt WebEngine"
}, },
"auth.msa.error.noXboxProfile": { "auth.msa.error.noXboxProfile": {
"ru": "К аккаунту Microsoft не привязан профиль Xbox. Создайте его на xbox.com и повторите вход.", "ru": "К аккаунту Microsoft не привязан профиль Xbox. Создайте его на xbox.com и повторите вход.",
@@ -196,8 +196,8 @@
"en": "The Microsoft session has expired — sign in to the account again (%1)" "en": "The Microsoft session has expired — sign in to the account again (%1)"
}, },
"auth.msa.error.windowUnavailable": { "auth.msa.error.windowUnavailable": {
"ru": "Окно входа Microsoft недоступно: сборка без Qt WebEngine", "ru": "Окно входа Microsoft недоступно",
"en": "The Microsoft sign-in window is unavailable: this build has no Qt WebEngine" "en": "The Microsoft sign-in window is unavailable"
}, },
"auth.msa.error.xboxNoToken": { "auth.msa.error.xboxNoToken": {
"ru": "Xbox Live не вернул токен", "ru": "Xbox Live не вернул токен",
@@ -288,8 +288,8 @@
"en": "Pick a version" "en": "Pick a version"
}, },
"build.error.closeGameFilesMoving": { "build.error.closeGameFilesMoving": {
"ru": "Сначала закройте игру — её файлы сейчас переносятся", "ru": "Файлы игры в процессе переноса, сначала закройте игру",
"en": "Close the game first — its files are being moved right now" "en": "Game files are being moved, close the game first"
}, },
"build.error.filesIncomplete": { "build.error.filesIncomplete": {
"ru": "Файлы сборки установлены не полностью: %1\nПовторите установку сборки.", "ru": "Файлы сборки установлены не полностью: %1\nПовторите установку сборки.",
@@ -328,8 +328,8 @@
"en": "The build was not switched: %1" "en": "The build was not switched: %1"
}, },
"build.error.switchFailedDirty": { "build.error.switchFailedDirty": {
"ru": "Сборка не переключена: %1\nСодержимое .minecraft осталось разобранным — повторите переключение.", "ru": "Сборка не переключена: %1\n Повторите переключение.",
"en": "The build was not switched: %1\nThe contents of .minecraft are left half-applied — switch again." "en": "The build was not switched: %1\n Switch again."
}, },
"build.error.versionNotInstalled": { "build.error.versionNotInstalled": {
"ru": "Версия «%1» из сборки «%2» не установлена в %3", "ru": "Версия «%1» из сборки «%2» не установлена в %3",
@@ -340,8 +340,8 @@
"en": "Build \"%1\" will be removed and cannot be recovered." "en": "Build \"%1\" will be removed and cannot be recovered."
}, },
"build.hint.removingLast": { "build.hint.removingLast": {
"ru": "Это последняя сборка. Содержимое .minecraft останется на месте.", "ru": "Последняя сборка, файлы из корня игры не удаляются.",
"en": "This is the last build. The contents of .minecraft will stay where they are." "en": "Last build. The contents of .minecraft will stay."
}, },
"build.label.active": { "build.label.active": {
"ru": "активна", "ru": "активна",
@@ -360,8 +360,8 @@
"en": "Server address" "en": "Server address"
}, },
"build.placeholder.name": { "build.placeholder.name": {
"ru": "как её видно в списке", "ru": "Введите наименование",
"en": "as it appears in the list" "en": "Write a name"
}, },
"build.placeholder.serverAddress": { "build.placeholder.serverAddress": {
"ru": "host:port (необязательно)", "ru": "host:port (необязательно)",
@@ -384,8 +384,8 @@
"en": "Build \"%1\": %2" "en": "Build \"%1\": %2"
}, },
"build.status.ready": { "build.status.ready": {
"ru": "Всё на месте, сборку можно запускать", "ru": "Готова к запуску",
"en": "Everything is in place, the build can be launched" "en": "Ready to launch"
}, },
"build.title.numbered": { "build.title.numbered": {
"ru": "Сборка %1", "ru": "Сборка %1",
@@ -396,8 +396,8 @@
"en": "Remove the build?" "en": "Remove the build?"
}, },
"build.warning.archiveGoesToo": { "build.warning.archiveGoesToo": {
"ru": "Вместе с ней удалится её архив: моды, конфиги и миры этой сборки восстановить будет нельзя.", "ru": "Внимание, при удалении так же очищаются пользовательские сохранённые архивы.",
"en": "Its archive goes with it: the mods, configs and worlds of this build cannot be recovered." "en": "Attention, after removing from the list all saved files will be deleted."
}, },
"build.warning.archiveNotRemoved": { "build.warning.archiveNotRemoved": {
"ru": "Сборка «%1» удалена, но её архив в %2 стереть не удалось", "ru": "Сборка «%1» удалена, но её архив в %2 стереть не удалось",