launcher: Home and the sidebar in French and Russian
The second part of the launcher's translation: Home's banner, rows, crash notice and Join a friend, the instance row and its actions, the sidebar's headings and instance states, the partner label, play times and "last played" dates, and the resources line. Play's label clears its icon in wider languages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+63
-1
@@ -91,5 +91,67 @@
|
||||
"Landscape": "Paysage",
|
||||
"Your picture, behind Home, cropped to fill it.": "Votre image, derrière l'accueil, recadrée pour le remplir.",
|
||||
"Horizon's landscape. Pick a PNG, JPEG or WebP picture of your own.": "Le paysage d'Horizon. Choisissez votre propre image PNG, JPEG ou WebP.",
|
||||
"Home background": "Fond de l'accueil"
|
||||
"Home background": "Fond de l'accueil",
|
||||
"Play": "Jouer",
|
||||
"Running": "En cours",
|
||||
"Stopped": "Arrêté",
|
||||
"Start the game, or run a server for others to join.": "Lancez le jeu, ou ouvrez un serveur que d'autres peuvent rejoindre.",
|
||||
"No version installed yet.": "Aucune version installée pour l'instant.",
|
||||
"Choose a version": "Choisir une version",
|
||||
"Host a server": "Héberger un serveur",
|
||||
"Preparing…": "Préparation…",
|
||||
"Prepare": "Préparer",
|
||||
"Prepare again": "Préparer à nouveau",
|
||||
"Not found. Choose the folder where your launcher keeps Minecraft.": "Introuvable. Choisissez le dossier où votre launcher garde Minecraft.",
|
||||
"Ready to play": "Prêt à jouer",
|
||||
"Resources": "Ressources",
|
||||
"Minecraft folder": "Dossier Minecraft",
|
||||
"Choose…": "Choisir…",
|
||||
"Playing offline": "Jeu hors ligne",
|
||||
"Online servers need a Microsoft account.": "Les serveurs en ligne demandent un compte Microsoft.",
|
||||
"Accounts…": "Comptes…",
|
||||
"The first preparation takes a few minutes.": "La première préparation prend quelques minutes.",
|
||||
"Horizon crashed": "Horizon a planté",
|
||||
"When you report it on Discord, attach the crash report file (or paste it with Copy report) and say what you were doing.": "Pour le signaler sur Discord, joignez le rapport de plantage (ou collez-le avec Copier le rapport) et dites ce que vous faisiez.",
|
||||
"The game ended without a crash report. When you report it on Discord, attach latest.log from the logs folder and say what you were doing.": "Le jeu s'est arrêté sans rapport de plantage. Pour le signaler sur Discord, joignez latest.log du dossier des journaux et dites ce que vous faisiez.",
|
||||
"Open report": "Ouvrir le rapport",
|
||||
"Copied": "Copié",
|
||||
"Copy report": "Copier le rapport",
|
||||
"Open logs folder": "Ouvrir le dossier des journaux",
|
||||
"Report on Discord": "Signaler sur Discord",
|
||||
"Dismiss": "Fermer",
|
||||
"INSTANCES": "INSTANCES",
|
||||
"SERVERS": "SERVEURS",
|
||||
"HIDE": "MASQUER",
|
||||
"Website": "Site web",
|
||||
"Ready": "Prêt",
|
||||
"Needs preparing": "À préparer",
|
||||
"{version} is not installed": "{version} n'est pas installée",
|
||||
"No version installed": "Aucune version installée",
|
||||
"Modpack": "Modpack",
|
||||
"From Horizon's registry": "Du registre d'Horizon",
|
||||
"Instance": "Instance",
|
||||
"Edit…": "Modifier…",
|
||||
"Copy…": "Copier…",
|
||||
"Export…": "Exporter…",
|
||||
"Close export": "Fermer l'export",
|
||||
"Launch…": "Lancement…",
|
||||
"Close launch": "Fermer le lancement",
|
||||
"Open folder": "Ouvrir le dossier",
|
||||
"Join a friend": "Rejoindre un ami",
|
||||
"Paste an invite code": "Collez un code d'invitation",
|
||||
"Paste the code a friend shows when they invite you.": "Collez le code qu'un ami affiche quand il vous invite.",
|
||||
"Join": "Rejoindre",
|
||||
"Shortcut": "Raccourci",
|
||||
"Remove shortcut": "Retirer le raccourci",
|
||||
"today": "aujourd'hui",
|
||||
"yesterday": "hier",
|
||||
"{days} days ago": "il y a {days} jours",
|
||||
"{time} in total · last played {when}": "{time} au total · joué pour la dernière fois {when}",
|
||||
"Not played yet": "Pas encore joué",
|
||||
"under a minute": "moins d'une minute",
|
||||
"PARTNER": "PARTENAIRE",
|
||||
"Not prepared. Horizon needs the files of your own Minecraft {version}.": "Pas préparé. Horizon a besoin des fichiers de votre propre Minecraft {version}.",
|
||||
"Prepared for Minecraft {version}": "Préparé pour Minecraft {version}",
|
||||
"Prepared for Minecraft {version}, without its sounds: the game will be silent. Prepare again to copy them from your Minecraft install.": "Préparé pour Minecraft {version}, sans ses sons : le jeu sera muet. Préparez à nouveau pour les copier depuis votre installation de Minecraft."
|
||||
}
|
||||
|
||||
+63
-1
@@ -91,5 +91,67 @@
|
||||
"Landscape": "Пейзаж",
|
||||
"Your picture, behind Home, cropped to fill it.": "Ваша картинка за главной страницей, обрезанная по размеру.",
|
||||
"Horizon's landscape. Pick a PNG, JPEG or WebP picture of your own.": "Пейзаж Horizon. Выберите свою картинку PNG, JPEG или WebP.",
|
||||
"Home background": "Фон главной"
|
||||
"Home background": "Фон главной",
|
||||
"Play": "Играть",
|
||||
"Running": "Запущено",
|
||||
"Stopped": "Остановлен",
|
||||
"Start the game, or run a server for others to join.": "Запустите игру или сервер, к которому смогут подключиться другие.",
|
||||
"No version installed yet.": "Пока не установлено ни одной версии.",
|
||||
"Choose a version": "Выбрать версию",
|
||||
"Host a server": "Открыть сервер",
|
||||
"Preparing…": "Подготовка…",
|
||||
"Prepare": "Подготовить",
|
||||
"Prepare again": "Подготовить заново",
|
||||
"Not found. Choose the folder where your launcher keeps Minecraft.": "Не найдено. Выберите папку, где ваш лаунчер хранит Minecraft.",
|
||||
"Ready to play": "Готово к игре",
|
||||
"Resources": "Ресурсы",
|
||||
"Minecraft folder": "Папка Minecraft",
|
||||
"Choose…": "Выбрать…",
|
||||
"Playing offline": "Игра офлайн",
|
||||
"Online servers need a Microsoft account.": "Для онлайн-серверов нужен аккаунт Microsoft.",
|
||||
"Accounts…": "Аккаунты…",
|
||||
"The first preparation takes a few minutes.": "Первая подготовка занимает несколько минут.",
|
||||
"Horizon crashed": "Horizon вылетел",
|
||||
"When you report it on Discord, attach the crash report file (or paste it with Copy report) and say what you were doing.": "Сообщая об этом в Discord, приложите файл отчёта о сбое (или вставьте его через «Скопировать отчёт») и расскажите, что вы делали.",
|
||||
"The game ended without a crash report. When you report it on Discord, attach latest.log from the logs folder and say what you were doing.": "Игра закрылась без отчёта о сбое. Сообщая об этом в Discord, приложите latest.log из папки журналов и расскажите, что вы делали.",
|
||||
"Open report": "Открыть отчёт",
|
||||
"Copied": "Скопировано",
|
||||
"Copy report": "Скопировать отчёт",
|
||||
"Open logs folder": "Открыть папку журналов",
|
||||
"Report on Discord": "Сообщить в Discord",
|
||||
"Dismiss": "Закрыть",
|
||||
"INSTANCES": "СБОРКИ",
|
||||
"SERVERS": "СЕРВЕРЫ",
|
||||
"HIDE": "СКРЫТЬ",
|
||||
"Website": "Сайт",
|
||||
"Ready": "Готово",
|
||||
"Needs preparing": "Нужна подготовка",
|
||||
"{version} is not installed": "{version} не установлена",
|
||||
"No version installed": "Версия не установлена",
|
||||
"Modpack": "Сборка модов",
|
||||
"From Horizon's registry": "Из каталога Horizon",
|
||||
"Instance": "Сборка",
|
||||
"Edit…": "Изменить…",
|
||||
"Copy…": "Копировать…",
|
||||
"Export…": "Экспорт…",
|
||||
"Close export": "Закрыть экспорт",
|
||||
"Launch…": "Запуск…",
|
||||
"Close launch": "Закрыть запуск",
|
||||
"Open folder": "Открыть папку",
|
||||
"Join a friend": "Присоединиться к другу",
|
||||
"Paste an invite code": "Вставьте код приглашения",
|
||||
"Paste the code a friend shows when they invite you.": "Вставьте код, который друг показывает, когда приглашает вас.",
|
||||
"Join": "Войти",
|
||||
"Shortcut": "Ярлык",
|
||||
"Remove shortcut": "Убрать ярлык",
|
||||
"today": "сегодня",
|
||||
"yesterday": "вчера",
|
||||
"{days} days ago": "{days} дн. назад",
|
||||
"{time} in total · last played {when}": "всего {time} · последняя игра: {when}",
|
||||
"Not played yet": "Ещё не запускалась",
|
||||
"under a minute": "меньше минуты",
|
||||
"PARTNER": "ПАРТНЁР",
|
||||
"Not prepared. Horizon needs the files of your own Minecraft {version}.": "Не подготовлено. Horizon нужны файлы вашего собственного Minecraft {version}.",
|
||||
"Prepared for Minecraft {version}": "Подготовлено для Minecraft {version}",
|
||||
"Prepared for Minecraft {version}, without its sounds: the game will be silent. Prepare again to copy them from your Minecraft install.": "Подготовлено для Minecraft {version}, но без звуков: игра будет беззвучной. Подготовьте заново, чтобы скопировать их из вашей установки Minecraft."
|
||||
}
|
||||
|
||||
@@ -651,14 +651,17 @@ fn sounds_in(resources: &Path) -> bool {
|
||||
|
||||
/// What the Play page says of a version's resources.
|
||||
pub fn resources_summary(prepared: bool, sounds: bool, version: &str) -> String {
|
||||
use crate::i18n::trf;
|
||||
match (prepared, sounds) {
|
||||
(false, _) => {
|
||||
format!("Not prepared. Horizon needs the files of your own Minecraft {version}.")
|
||||
}
|
||||
(true, true) => format!("Prepared for Minecraft {version}"),
|
||||
(true, false) => format!(
|
||||
(false, _) => trf(
|
||||
"Not prepared. Horizon needs the files of your own Minecraft {version}.",
|
||||
&[("version", version)],
|
||||
),
|
||||
(true, true) => trf("Prepared for Minecraft {version}", &[("version", version)]),
|
||||
(true, false) => trf(
|
||||
"Prepared for Minecraft {version}, without its sounds: the game will be silent. \
|
||||
Prepare again to copy them from your Minecraft install."
|
||||
Prepare again to copy them from your Minecraft install.",
|
||||
&[("version", version)],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ fn record_play_in(
|
||||
pub fn play_time(played: Duration) -> String {
|
||||
let minutes = played.as_secs() / 60;
|
||||
match (minutes / 60, minutes % 60) {
|
||||
(0, 0) => "under a minute".into(),
|
||||
(0, 0) => crate::i18n::tr("under a minute").into(),
|
||||
(0, m) => format!("{m} min"),
|
||||
(h, m) if h < 100 => format!("{h} h {m:02} min"),
|
||||
(h, _) => format!("{h} h"),
|
||||
|
||||
@@ -190,11 +190,11 @@ impl Launcher {
|
||||
InstanceKind::Default | InstanceKind::Own => (
|
||||
self.page == Page::Play && selected.as_ref() == Some(&i.id),
|
||||
match (&tag, &i.version) {
|
||||
(None, Some(own)) => format!("{own} is not installed"),
|
||||
(None, None) => "No version installed".into(),
|
||||
(Some(_), _) if !prepared => "Needs preparing".into(),
|
||||
(Some(tag), Some(_)) => format!("Ready · {tag}"),
|
||||
(Some(_), None) => "Ready".into(),
|
||||
(None, Some(own)) => trf("{version} is not installed", &[("version", own)]),
|
||||
(None, None) => tr("No version installed").into(),
|
||||
(Some(_), _) if !prepared => tr("Needs preparing").into(),
|
||||
(Some(tag), Some(_)) => format!("{} · {tag}", tr("Ready")),
|
||||
(Some(_), None) => tr("Ready").into(),
|
||||
},
|
||||
),
|
||||
InstanceKind::Port => {
|
||||
@@ -202,10 +202,10 @@ impl Launcher {
|
||||
let summary = self.packs.iter().find(|p| p.slug == slug);
|
||||
(
|
||||
on_packs && self.pack_open.as_deref() == Some(slug),
|
||||
summary.map_or_else(|| "Modpack".into(), |p| p.summary()),
|
||||
summary.map_or_else(|| tr("Modpack").into(), |p| p.summary()),
|
||||
)
|
||||
}
|
||||
InstanceKind::Pack => (false, "From Horizon's registry".into()),
|
||||
InstanceKind::Pack => (false, tr("From Horizon's registry").into()),
|
||||
};
|
||||
let dot = matches!(i.kind, InstanceKind::Default | InstanceKind::Own).then(|| {
|
||||
div()
|
||||
@@ -621,7 +621,7 @@ impl Launcher {
|
||||
);
|
||||
Some(index_row(
|
||||
n,
|
||||
"Instance",
|
||||
tr("Instance"),
|
||||
Some(
|
||||
div()
|
||||
.flex()
|
||||
@@ -651,12 +651,14 @@ impl Launcher {
|
||||
/// "3 h 05 min in total · last played today", or "Not played yet".
|
||||
fn played_line(instance: &Instance) -> String {
|
||||
match instance.last_played {
|
||||
Some(when) => format!(
|
||||
"{} in total · last played {}",
|
||||
instances::play_time(instance.played),
|
||||
worlds_page::ago(when)
|
||||
Some(when) => trf(
|
||||
"{time} in total · last played {when}",
|
||||
&[
|
||||
("time", &instances::play_time(instance.played)),
|
||||
("when", &worlds_page::ago(when)),
|
||||
],
|
||||
),
|
||||
None => "Not played yet".into(),
|
||||
None => tr("Not played yet").into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+25
-21
@@ -1755,7 +1755,7 @@ impl Launcher {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(4.))
|
||||
.child(heading("INSTANCES".into()).child(add))
|
||||
.child(heading(tr("INSTANCES").into()).child(add))
|
||||
.children(self.instance_items(heading, cx));
|
||||
let servers: Vec<_> = self
|
||||
.servers
|
||||
@@ -1764,7 +1764,7 @@ impl Launcher {
|
||||
.unwrap_or_else(|| self.side_servers.clone());
|
||||
let mut list = list;
|
||||
if !servers.is_empty() {
|
||||
list = list.child(heading("SERVERS".into()));
|
||||
list = list.child(heading(tr("SERVERS").into()));
|
||||
}
|
||||
for e in servers {
|
||||
let up = self.servers.is_up(&e.dir);
|
||||
@@ -1790,7 +1790,7 @@ impl Launcher {
|
||||
.flex_col()
|
||||
.min_w_0()
|
||||
.child(title(e.name.clone()))
|
||||
.child(sub(if up { "Running" } else { "Stopped" }.into())),
|
||||
.child(sub(tr(if up { "Running" } else { "Stopped" }).into())),
|
||||
)
|
||||
.on_click(cx.listener(move |this, _: &ClickEvent, _, cx| {
|
||||
this.servers.open = Some(dir.clone());
|
||||
@@ -1874,7 +1874,7 @@ impl Launcher {
|
||||
text(theme::MICRO)
|
||||
.text_color(t.muted)
|
||||
.hover(move |s| s.text_color(t.ink))
|
||||
.child("HIDE"),
|
||||
.child(tr("HIDE")),
|
||||
)
|
||||
.on_click(cx.listener(|this, _: &ClickEvent, _, cx| {
|
||||
this.settings.partner_hidden = true;
|
||||
@@ -1896,7 +1896,7 @@ impl Launcher {
|
||||
if !self.links.website.is_empty() {
|
||||
foot = foot.child(link(
|
||||
"website",
|
||||
"Website",
|
||||
tr("Website"),
|
||||
"globe",
|
||||
self.links.website.clone(),
|
||||
));
|
||||
@@ -2061,7 +2061,7 @@ impl Launcher {
|
||||
"Start the game, or run a server for others to join.",
|
||||
&t,
|
||||
))
|
||||
.child(centered("No version installed yet.", &t).child(
|
||||
.child(centered(tr("No version installed yet."), &t).child(
|
||||
button("go-versions", "Choose a version", Kind::Secondary, &t).on_click(
|
||||
cx.listener(|this, _: &ClickEvent, _, cx| {
|
||||
this.go(Page::Versions);
|
||||
@@ -2077,10 +2077,12 @@ impl Launcher {
|
||||
let hero = self.hero(&tag, &version, prepared, cx);
|
||||
let running = self.client.is_some() || self.starting_client.is_some();
|
||||
let play_label = if running { "Running" } else { "Play" };
|
||||
// The label clears the icon in every language ("Играть" is wider than "Play").
|
||||
let mut play = button("play", play_label, Kind::Primary, &t)
|
||||
.min_w(px(200.))
|
||||
.h(px(60.))
|
||||
.px(px(28.))
|
||||
.pl(px(60.))
|
||||
.pr(px(28.))
|
||||
.text_size(px(28.))
|
||||
.font_weight(FontWeight::SEMIBOLD)
|
||||
.child(
|
||||
@@ -2121,7 +2123,7 @@ impl Launcher {
|
||||
);
|
||||
|
||||
let resources_value = if self.preparing {
|
||||
"Preparing…".to_string()
|
||||
tr("Preparing…").to_string()
|
||||
} else {
|
||||
backend::resources_summary(prepared, !silent, &version)
|
||||
};
|
||||
@@ -2146,7 +2148,9 @@ impl Launcher {
|
||||
let minecraft = self.settings.minecraft_dir.as_deref().map_or_else(
|
||||
|| {
|
||||
div()
|
||||
.child("Not found. Choose the folder where your launcher keeps Minecraft.")
|
||||
.child(tr(
|
||||
"Not found. Choose the folder where your launcher keeps Minecraft.",
|
||||
))
|
||||
.into_any_element()
|
||||
},
|
||||
|p| {
|
||||
@@ -2165,14 +2169,14 @@ impl Launcher {
|
||||
.children(self.launch_panel(cx))
|
||||
.child(index_row(
|
||||
2,
|
||||
"Resources",
|
||||
tr("Resources"),
|
||||
Some(div().child(resources_value).into_any_element()),
|
||||
Some(prepare.into_any_element()),
|
||||
&t,
|
||||
))
|
||||
.child(index_row(
|
||||
3,
|
||||
"Minecraft folder",
|
||||
tr("Minecraft folder"),
|
||||
Some(minecraft),
|
||||
Some(
|
||||
button("choose-mc", "Choose…", Kind::Ghost, &t)
|
||||
@@ -2188,10 +2192,10 @@ impl Launcher {
|
||||
if self.accounts.selected().is_none() && account::client_id().is_some() {
|
||||
rows = rows.child(index_row(
|
||||
5,
|
||||
"Playing offline",
|
||||
tr("Playing offline"),
|
||||
Some(
|
||||
div()
|
||||
.child("Online servers need a Microsoft account.")
|
||||
.child(tr("Online servers need a Microsoft account."))
|
||||
.into_any_element(),
|
||||
),
|
||||
Some(
|
||||
@@ -2276,7 +2280,7 @@ impl Launcher {
|
||||
.child(
|
||||
text(theme::CAPTION)
|
||||
.text_color(t.muted)
|
||||
.child("The first preparation takes a few minutes."),
|
||||
.child(tr("The first preparation takes a few minutes.")),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -2370,11 +2374,11 @@ impl Launcher {
|
||||
},
|
||||
)),
|
||||
);
|
||||
let help = if crash.report.is_some() {
|
||||
let help = tr(if crash.report.is_some() {
|
||||
"When you report it on Discord, attach the crash report file (or paste it with Copy report) and say what you were doing."
|
||||
} else {
|
||||
"The game ended without a crash report. When you report it on Discord, attach latest.log from the logs folder and say what you were doing."
|
||||
};
|
||||
});
|
||||
let mut body = div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
@@ -2384,7 +2388,7 @@ impl Launcher {
|
||||
.child(
|
||||
text(theme::HEADING)
|
||||
.text_color(t.ink)
|
||||
.child("Horizon crashed"),
|
||||
.child(tr("Horizon crashed")),
|
||||
);
|
||||
if !crash.summary.is_empty() {
|
||||
body = body.child(div().text_color(t.ink_soft).child(crash.summary));
|
||||
@@ -2460,7 +2464,7 @@ impl Launcher {
|
||||
.child(caps(format!("Minecraft {version}")).text_color(t.ink_soft))
|
||||
.child(div().size(px(4.)).bg(t.muted))
|
||||
.child(
|
||||
caps(if prepared { "Ready" } else { "Needs preparing" })
|
||||
caps(tr(if prepared { "Ready" } else { "Needs preparing" }))
|
||||
.text_color(if prepared { t.success } else { t.warn }),
|
||||
),
|
||||
)
|
||||
@@ -3153,7 +3157,7 @@ impl Launcher {
|
||||
.text_size(px(12.))
|
||||
.cursor_text()
|
||||
.child(if self.friend_code.is_empty() && !focused {
|
||||
div().text_color(t.muted).child("Paste an invite code")
|
||||
div().text_color(t.muted).child(tr("Paste an invite code"))
|
||||
} else {
|
||||
// The end of a long code stays in view while typing.
|
||||
let shown: String = {
|
||||
@@ -3203,10 +3207,10 @@ impl Launcher {
|
||||
}
|
||||
index_row(
|
||||
4,
|
||||
"Join a friend",
|
||||
tr("Join a friend"),
|
||||
Some(
|
||||
div()
|
||||
.child("Paste the code a friend shows when they invite you.")
|
||||
.child(tr("Paste the code a friend shows when they invite you."))
|
||||
.into_any_element(),
|
||||
),
|
||||
Some(
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn banner(id: &'static str, width: f32, t: &Theme) -> Stateful<Div> {
|
||||
crate::text(crate::theme::MICRO)
|
||||
.text_color(t.muted)
|
||||
.group_hover(id, move |s| s.text_color(t.ink))
|
||||
.child("PARTNER"),
|
||||
.child(horizon_launcher::i18n::tr("PARTNER")),
|
||||
)
|
||||
.child(
|
||||
div().relative().group_hover(id, |s| s.top(px(-2.))).child(
|
||||
|
||||
@@ -32,9 +32,9 @@ pub(super) fn ago(when: std::time::SystemTime) -> String {
|
||||
let when: chrono::DateTime<chrono::Local> = when.into();
|
||||
let days = (chrono::Local::now().date_naive() - when.date_naive()).num_days();
|
||||
match days {
|
||||
..=0 => "today".into(),
|
||||
1 => "yesterday".into(),
|
||||
2..=13 => format!("{days} days ago"),
|
||||
..=0 => tr("today").into(),
|
||||
1 => tr("yesterday").into(),
|
||||
2..=13 => trf("{days} days ago", &[("days", &days.to_string())]),
|
||||
_ => when.format("%Y-%m-%d").to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user