feat(i18n): multiple localization changes

Improved `tr!` macro to use enhanced arguments syntax;
Updated Indonesian;
Fixed formatting error for `close` string
This commit is contained in:
Observer KRypt0n_ 2023-08-02 15:32:33 +02:00
parent cab5a1f6f7
commit 7614561213
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
11 changed files with 88 additions and 59 deletions

View file

@ -13,7 +13,7 @@ failed-get-selected-wine = Gagal mendapatkan versi wine yang dipilih
downloading-failed = Gagal mengunduh downloading-failed = Gagal mengunduh
unpacking-failed = Gagal mengekstrak berkas unpacking-failed = Gagal mengekstrak berkas
kill-game-process-failed = Failed to kill the game's process kill-game-process-failed = Gagal menghentikan paksa proses game
game-file-repairing-error = Gagal memperbaiki file game game-file-repairing-error = Gagal memperbaiki file game
integrity-files-getting-error = Gagal mendapatkan integritas file integrity-files-getting-error = Gagal mendapatkan integritas file

View file

@ -58,8 +58,8 @@ disable-mhypbase-description = Masih dalam pengujicobaan. Jika diaktifkan, launc
ask-superuser-permissions = Minta izin dari admin ask-superuser-permissions = Minta izin dari admin
ask-superuser-permissions-description = Launcher akan otomatis memperbarui file hosts Anda. Opsi ini tidak diperlukan jika Anda memakai edisi flatpak ask-superuser-permissions-description = Launcher akan otomatis memperbarui file hosts Anda. Opsi ini tidak diperlukan jika Anda memakai edisi flatpak
launcher-behavior = Launcher behavior launcher-behavior = Kelakuan launcher
launcher-behavior-description = What should launcher window do when it starts the game launcher-behavior-description = Apa yang terjadi pada jendela launcher ketika memulai game
wine-tools = Peralatan wine wine-tools = Peralatan wine
command-line = Command line command-line = Command line

View file

@ -16,10 +16,17 @@ debug-file = File debug
wish-url = Buka wishes wish-url = Buka wishes
about = Tentang about = Tentang
close = { $form ->
[verb] Menutup
*[noun] Tutup
}
close = Tutup hide = { $form ->
hide = Hide [verb] Sembunyikan
nothing = Nothing *[noun] Sembunyi
}
nothing = Tidak berubah
save = Simpan save = Simpan
continue = Lanjutkan continue = Lanjutkan
resume = Lanjutkan resume = Lanjutkan
@ -64,7 +71,7 @@ update = Perbarui
download = Unduh download = Unduh
predownload-update = Pra-unduh pembaruan versi {$version} ({$size}) predownload-update = Pra-unduh pembaruan versi {$version} ({$size})
kill-game-process = Kill game process kill-game-process = Hentikan paksa proses game
main-window--patch-unavailable-tooltip = Server patch tidak tersedia dan launcher tidak bisa memverifikasi status patch game. Anda bisa menjalankan game dengan resiko sendiri main-window--patch-unavailable-tooltip = Server patch tidak tersedia dan launcher tidak bisa memverifikasi status patch game. Anda bisa menjalankan game dengan resiko sendiri
main-window--patch-outdated-tooltip = Patch kadaluarsa atau sedang dalam persiapan sehingga tidak tersedia. Kembali lagi nanti untuk melihat status patch main-window--patch-outdated-tooltip = Patch kadaluarsa atau sedang dalam persiapan sehingga tidak tersedia. Kembali lagi nanti untuk melihat status patch

View file

@ -88,10 +88,10 @@ pub fn format_lang(lang: &LanguageIdentifier) -> String {
/// With parameters: /// With parameters:
/// ///
/// ```no_run /// ```no_run
/// println!("Translated message: {}", tr!("game-outdated", [ /// println!("Translated message: {}", tr!("game-outdated", {
/// ("latest", "3.3.0") /// "latest" = "3.3.0"
/// ])); /// }));
/// ``` /// ```
macro_rules! tr { macro_rules! tr {
($id:expr) => { ($id:expr) => {
{ {
@ -104,19 +104,22 @@ macro_rules! tr {
} }
}; };
($id:expr, $args:expr) => { ($id:expr, { $($key:literal = $value:expr),* }) => {
{ {
use std::collections::HashMap;
use fluent_templates::Loader; use fluent_templates::Loader;
use fluent_templates::fluent_bundle::FluentValue; use fluent_templates::fluent_bundle::FluentValue;
let mut args = HashMap::new();
$(
args.insert($key, FluentValue::from($value));
)*
#[allow(unused_unsafe)] #[allow(unused_unsafe)]
$crate::i18n::LOCALES $crate::i18n::LOCALES
.lookup_with_args( .lookup_complete(unsafe { &$crate::i18n::LANG }, $id, Some(&args))
unsafe { &$crate::i18n::LANG },
$id,
&std::collections::HashMap::from_iter($args.into_iter()
.map(|(key, value)| (key, FluentValue::from(value))))
)
.expect(&format!("Failed to find a message with given id: {}", stringify!($id))) .expect(&format!("Failed to find a message with given id: {}", stringify!($id)))
} }
}; };

View file

@ -98,6 +98,9 @@ fn main() {
// Forcely run the game // Forcely run the game
let just_run_game = std::env::args().any(|arg| &arg == "--just-run-game"); let just_run_game = std::env::args().any(|arg| &arg == "--just-run-game");
// Forcely disable verbode tracing output in stdout
let no_verbose_tracing = std::env::args().any(|arg| &arg == "--no-verbose-tracing");
// Prepare stdout logger // Prepare stdout logger
let stdout = tracing_subscriber::fmt::layer() let stdout = tracing_subscriber::fmt::layer()
.pretty() .pretty()
@ -108,8 +111,8 @@ fn main() {
LevelFilter::WARN LevelFilter::WARN
} }
}) })
.with_filter(filter_fn(|metadata| { .with_filter(filter_fn(move |metadata| {
!metadata.target().contains("rustls") !metadata.target().contains("rustls") && !no_verbose_tracing
})); }));
// Prepare debug file logger // Prepare debug file logger

View file

@ -243,7 +243,7 @@ impl SimpleAsyncComponent for DefaultPathsApp {
gtk::Button { gtk::Button {
set_label: &if model.migrate_installation { set_label: &if model.migrate_installation {
tr!("close") tr!("close", { "form" = "noun" })
} else { } else {
tr!("exit") tr!("exit")
}, },

View file

@ -175,7 +175,9 @@ impl SimpleAsyncComponent for DependenciesApp {
for package in packages { for package in packages {
if !is_available(package) { if !is_available(package) {
sender.output(Self::Output::Toast { sender.output(Self::Output::Toast {
title: tr!("package-not-available", [("package", package)]), title: tr!("package-not-available", {
"package" = package
}),
description: None description: None
}); });

View file

@ -285,7 +285,7 @@ impl SimpleComponent for FirstRunApp {
let dialog = adw::MessageDialog::new(MAIN_WINDOW.as_ref(), Some(&title), Some(&description)); let dialog = adw::MessageDialog::new(MAIN_WINDOW.as_ref(), Some(&title), Some(&description));
dialog.add_response("close", &tr!("close")); dialog.add_response("close", &tr!("close", { "form" = "noun" }));
dialog.add_response("save", &tr!("save")); dialog.add_response("save", &tr!("save"));
dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested); dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested);

View file

@ -308,13 +308,13 @@ impl SimpleComponent for App {
set_width_request: 44, set_width_request: 44,
#[watch] #[watch]
set_tooltip_text: Some(&tr!("predownload-update", [ set_tooltip_text: Some(&tr!("predownload-update", {
("version", match model.state.as_ref() { "version" = match model.state.as_ref() {
Some(LauncherState::PredownloadAvailable { game, .. }) => game.latest().to_string(), Some(LauncherState::PredownloadAvailable { game, .. }) => game.latest().to_string(),
_ => String::from("?") _ => String::from("?")
}), },
("size", match model.state.as_ref() { "size" = match model.state.as_ref() {
Some(LauncherState::PredownloadAvailable { game, voices }) => { Some(LauncherState::PredownloadAvailable { game, voices }) => {
let mut size = game.downloaded_size().unwrap_or(0); let mut size = game.downloaded_size().unwrap_or(0);
@ -326,8 +326,8 @@ impl SimpleComponent for App {
} }
_ => String::from("?") _ => String::from("?")
}) }
])), })),
#[watch] #[watch]
set_visible: matches!(model.state.as_ref(), Some(LauncherState::PredownloadAvailable { .. })), set_visible: matches!(model.state.as_ref(), Some(LauncherState::PredownloadAvailable { .. })),
@ -1002,9 +1002,9 @@ impl SimpleComponent for App {
} }
StateUpdating::Voice(locale) => { StateUpdating::Voice(locale) => {
sender.input(AppMsg::SetLoadingStatus(Some(Some(tr!("loading-launcher-state--voice", [ sender.input(AppMsg::SetLoadingStatus(Some(Some(tr!("loading-launcher-state--voice", {
("locale", locale.to_name()) "locale" = locale.to_name()
]))))); })))));
} }
StateUpdating::Patch => { StateUpdating::Patch => {
@ -1189,7 +1189,7 @@ impl App {
Some(description.as_ref()) Some(description.as_ref())
); );
dialog.add_response("close", &tr!("close")); dialog.add_response("close", &tr!("close", { "form" = "noun" }));
dialog.add_response("save", &tr!("save")); dialog.add_response("save", &tr!("save"));
dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested); dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested);

View file

@ -397,10 +397,10 @@ impl SimpleAsyncComponent for GeneralApp {
#[watch] #[watch]
set_css_classes: match model.game_diff.as_ref() { set_css_classes: match model.game_diff.as_ref() {
Some(diff) => match diff { Some(diff) => match diff {
VersionDiff::Latest { .. } => &["success"], VersionDiff::Latest { .. } => &["success"],
VersionDiff::Predownload { .. } => &["accent"], VersionDiff::Predownload { .. } => &["accent"],
VersionDiff::Diff { .. } => &["warning"], VersionDiff::Diff { .. } => &["warning"],
VersionDiff::Outdated { .. } => &["error"], VersionDiff::Outdated { .. } => &["error"],
VersionDiff::NotInstalled { .. } => &[] VersionDiff::NotInstalled { .. } => &[]
} }
@ -411,17 +411,21 @@ impl SimpleAsyncComponent for GeneralApp {
set_tooltip_text: Some(&match model.game_diff.as_ref() { set_tooltip_text: Some(&match model.game_diff.as_ref() {
Some(diff) => match diff { Some(diff) => match diff {
VersionDiff::Latest { .. } => String::new(), VersionDiff::Latest { .. } => String::new(),
VersionDiff::Predownload { current, latest, .. } => tr!("game-predownload-available", [
("old", current.to_string()), VersionDiff::Predownload { current, latest, .. } => tr!("game-predownload-available", {
("new", latest.to_string()) "old" = current.to_string(),
]), "new" = latest.to_string()
VersionDiff::Diff { current, latest, .. } => tr!("game-update-available", [ }),
("old", current.to_string()),
("new", latest.to_string()) VersionDiff::Diff { current, latest, .. } => tr!("game-update-available", {
]), "old" = current.to_string(),
VersionDiff::Outdated { latest, ..} => tr!("game-outdated", [ "new" = latest.to_string()
("latest", latest.to_string()) }),
]),
VersionDiff::Outdated { latest, ..} => tr!("game-outdated", {
"latest" = latest.to_string()
}),
VersionDiff::NotInstalled { .. } => String::new() VersionDiff::NotInstalled { .. } => String::new()
} }
@ -439,8 +443,13 @@ impl SimpleAsyncComponent for GeneralApp {
set_text: &match model.player_patch.as_ref() { set_text: &match model.player_patch.as_ref() {
Some(patch) => match patch.status() { Some(patch) => match patch.status() {
PatchStatus::NotAvailable => tr!("patch-not-available"), PatchStatus::NotAvailable => tr!("patch-not-available"),
PatchStatus::Outdated { current, .. } => tr!("patch-outdated", [("current", current.to_string())]),
PatchStatus::Outdated { current, .. } => tr!("patch-outdated", {
"current" = current.to_string()
}),
PatchStatus::Preparation { .. } => tr!("patch-preparation"), PatchStatus::Preparation { .. } => tr!("patch-preparation"),
PatchStatus::Testing { version, .. } | PatchStatus::Testing { version, .. } |
PatchStatus::Available { version, .. } => version.to_string() PatchStatus::Available { version, .. } => version.to_string()
} }
@ -452,9 +461,11 @@ impl SimpleAsyncComponent for GeneralApp {
set_css_classes: match model.player_patch.as_ref() { set_css_classes: match model.player_patch.as_ref() {
Some(patch) => match patch.status() { Some(patch) => match patch.status() {
PatchStatus::NotAvailable => &["error"], PatchStatus::NotAvailable => &["error"],
PatchStatus::Outdated { .. } | PatchStatus::Outdated { .. } |
PatchStatus::Preparation { .. } | PatchStatus::Preparation { .. } |
PatchStatus::Testing { .. } => &["warning"], PatchStatus::Testing { .. } => &["warning"],
PatchStatus::Available { .. } => unsafe { PatchStatus::Available { .. } => unsafe {
let path = match Config::get() { let path = match Config::get() {
Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(),
@ -476,12 +487,15 @@ impl SimpleAsyncComponent for GeneralApp {
set_tooltip_text: Some(&match model.player_patch.as_ref() { set_tooltip_text: Some(&match model.player_patch.as_ref() {
Some(patch) => match patch.status() { Some(patch) => match patch.status() {
PatchStatus::NotAvailable => tr!("patch-not-available-tooltip"), PatchStatus::NotAvailable => tr!("patch-not-available-tooltip"),
PatchStatus::Outdated { current, latest, .. } => tr!("patch-outdated-tooltip", [
("current", current.to_string()), PatchStatus::Outdated { current, latest, .. } => tr!("patch-outdated-tooltip", {
("latest", latest.to_string()) "current" = current.to_string(),
]), "latest" = latest.to_string()
}),
PatchStatus::Preparation { .. } => tr!("patch-preparation-tooltip"), PatchStatus::Preparation { .. } => tr!("patch-preparation-tooltip"),
PatchStatus::Testing { .. } => tr!("patch-testing-tooltip"), PatchStatus::Testing { .. } => tr!("patch-testing-tooltip"),
PatchStatus::Available { .. } => unsafe { PatchStatus::Available { .. } => unsafe {
let path = match Config::get() { let path = match Config::get() {
Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(),
@ -582,8 +596,8 @@ impl SimpleAsyncComponent for GeneralApp {
set_model: Some(&gtk::StringList::new(&[ set_model: Some(&gtk::StringList::new(&[
&tr!("nothing"), &tr!("nothing"),
&tr!("hide", [("form", "verb")]), &tr!("hide", { "form" = "verb" }),
&tr!("close", [("form", "verb")]), &tr!("close", { "form" = "verb" }),
])), ])),
set_selected: match CONFIG.launcher.behavior { set_selected: match CONFIG.launcher.behavior {
@ -873,9 +887,9 @@ impl SimpleAsyncComponent for GeneralApp {
if let Err(err) = result { if let Err(err) = result {
sender.input(GeneralAppMsg::Toast { sender.input(GeneralAppMsg::Toast {
title: tr!("wine-run-error", [ title: tr!("wine-run-error", {
("executable", executable.join(" ")) "executable" = executable.join(" ")
]), }),
description: Some(err.to_string()) description: Some(err.to_string())
}); });

View file

@ -154,7 +154,7 @@ impl SimpleAsyncComponent for PreferencesApp {
let dialog = adw::MessageDialog::new(PREFERENCES_WINDOW.as_ref(), Some(&title), Some(&description)); let dialog = adw::MessageDialog::new(PREFERENCES_WINDOW.as_ref(), Some(&title), Some(&description));
dialog.add_response("close", &tr!("close")); dialog.add_response("close", &tr!("close", { "form" = "noun" }));
dialog.add_response("save", &tr!("save")); dialog.add_response("save", &tr!("save"));
dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested); dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested);