Skip to content

Commit

Permalink
Implemented localization with fluent.
Browse files Browse the repository at this point in the history
  • Loading branch information
mkrueger committed Aug 2, 2023
1 parent 4ac2224 commit 9f496c0
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 62 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ needle = "0.1.1"
threadpool = "1.0"
sudo = "0.6.0"
gabi = "0.2.6"
i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"]}
i18n-embed-fl = "0.6.4"
once_cell = "1.16.0"
rust-embed = "6.4.2"

[target.'cfg(target_os = "windows")'.dependencies]
proc-maps = "0.3.1"
Expand Down
5 changes: 5 additions & 0 deletions i18n.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

fallback_language = "en"

[fluent]
assets_dir = "i18n"
52 changes: 52 additions & 0 deletions i18n/de/game_cheetah.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
process-label = Prozesse:
no-processes-label = <Prozess nicht gesetzt>
filter-processes-hint = Prozesse filtern
default-label = Standard
name-label = Name:
value-label = Wert:
search-description-label = Suchbeschreibung
search-value-label = Suche nach { $valuetype }
found-one-result-label = 1 Vorkommen gefunden
found-results-label = { $results } Vorkommen gefunden
no-results-label = Keine Vorkommen
undo-button = Rückgängig
initial-search-button = Erste Suche
update-button = Aktualisieren
clear-button = Löschen
close-button = Schließen
hide-results-button = Ergebnisse verstecken
show-results-button = Ergebnisse zeigen
generic-error-label = <Fehler>
invalid-input-error = Eingabe ungültig
invalid-number-error = Zahl ungültig
conversion-error = Fehler beim Konvertieren { $valuetype }: { $message }
guess-value-item = Unklar (2-8 Bytes)
short-value-item = Short (2 Bytes)
int-value-item = Int (4 Bytes)
int64-value-item = Int64 (8 Bytes)
float-value-item = Float (4 Bytes)
double-value-item = Double (8 Bytes)
guess-descr = Unklar
short-descr = Short
int-descr = Int
int64-descr = Int64
float-descr = Float
double-descr = Double
address-heading = Addresse
value-heading = Wert
freezed-heading = Eingefroren
pid-heading = Pid
name-heading = Name
memory-heading = Speicher
command-heading = Kommando
update-numbers-progress = Aktualisiere { $current }/{ $total }
search-memory-progress = Suche { $current }/{ $total }
52 changes: 52 additions & 0 deletions i18n/en/game_cheetah.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
process-label = Processes:
no-processes-label = <no process set>
filter-processes-hint = Filter processes
default-label = default
name-label = Name:
value-label = Value:
search-description-label = Search description
search-value-label = Search for { $valuetype } value
found-one-result-label = found one result.
found-results-label = found { $results } results.
no-results-label = No results found.
undo-button = Undo
initial-search-button = Initial search
update-button = Update
clear-button = Clear
close-button = Close
hide-results-button = Hide Results
show-results-button = Show Results
generic-error-label = <error>
invalid-input-error = Invalid input
invalid-number-error = Invalid number
conversion-error = Error converting { $valuetype }: { $message }
guess-value-item = guess value (2-8 bytes)
short-value-item = short (2 bytes)
int-value-item = int (4 bytes)
int64-value-item = int64 (8 bytes)
float-value-item = float (4 bytes)
double-value-item = double (8 bytes)
guess-descr = Guess
short-descr = short
int-descr = int
int64-descr = int64
float-descr = float
double-descr = double
address-heading = Address
value-heading = Value
freezed-heading = Freezed
pid-heading = Pid
name-heading = Name
memory-heading = Memory
command-heading = Command
update-numbers-progress = Update { $current }/{ $total }
search-memory-progress = Search { $current }/{ $total }
89 changes: 47 additions & 42 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use egui::{Color32, RichText};
use egui_extras::{Column, TableBuilder};
use i18n_embed_fl::fl;
use process_memory::*;
use std::{
cmp::max,
Expand All @@ -25,9 +26,9 @@ impl GameCheetahEngine {
ui.spacing_mut().item_spacing = egui::Vec2::splat(20.0);
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing = egui::Vec2::splat(5.0);

let i = ui.add(
egui::TextEdit::singleline(&mut self.process_filter).hint_text("Filter processes"),
egui::TextEdit::singleline(&mut self.process_filter).hint_text(fl!(crate::LANGUAGE_LOADER, "filter-processes-hint")),
);
if ui.memory(|m| m.focus().is_none()) {
ui.memory_mut(|m| m.request_focus(i.id));
Expand All @@ -39,7 +40,7 @@ impl GameCheetahEngine {
self.process_filter.clear();
}

if ui.button("Close").clicked() {
if ui.button(fl!(crate::LANGUAGE_LOADER, "close-button")).clicked() {
self.show_process_window = false;
}
});
Expand All @@ -56,16 +57,16 @@ impl GameCheetahEngine {
table
.header(20.0, |mut header| {
header.col(|ui| {
ui.heading("Pid");
ui.heading(fl!(crate::LANGUAGE_LOADER, "pid-heading"));
});
header.col(|ui| {
ui.heading("Name");
ui.heading(fl!(crate::LANGUAGE_LOADER, "name-heading"));
});
header.col(|ui| {
ui.heading("Memory");
ui.heading(fl!(crate::LANGUAGE_LOADER, "memory-heading"));
});
header.col(|ui| {
ui.heading("Command");
ui.heading(fl!(crate::LANGUAGE_LOADER, "command-heading"));
});
})
.body(|mut body| {
Expand Down Expand Up @@ -126,13 +127,19 @@ impl eframe::App for GameCheetahEngine {
ui.spacing_mut().item_spacing = egui::Vec2::splat(12.0);
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing = egui::Vec2::splat(5.0);
ui.label("Process:");
ui.label(fl!(
crate::LANGUAGE_LOADER,
"process-label"
));

if ui
.button(if self.pid != 0 {
format!("{} ({})", self.process_name, self.pid)
} else {
"<no process set>".to_string()
fl!(
crate::LANGUAGE_LOADER,
"no-processes-label"
)
})
.clicked()
{
Expand All @@ -151,7 +158,7 @@ impl eframe::App for GameCheetahEngine {
.unwrap_or_default();
self.searches.clear();
self.searches
.push(Box::new(SearchContext::new("default".to_string())));
.push(Box::new(SearchContext::new( fl!(crate::LANGUAGE_LOADER, "default-label"))));
self.process_filter.clear();
}
});
Expand Down Expand Up @@ -207,11 +214,11 @@ impl GameCheetahEngine {
if self.searches.len() > 1 {
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing = egui::Vec2::splat(5.0);
ui.label("Name:");
ui.label(fl!(crate::LANGUAGE_LOADER, "name-label"));
if let Some(search_context) = self.searches.get_mut(search_index) {
ui.add(
egui::TextEdit::singleline(&mut search_context.description)
.hint_text("Search description")
.hint_text(fl!(crate::LANGUAGE_LOADER, "search-description-label"))
.interactive(matches!(search_context.searching, SearchMode::None)),
);
}
Expand All @@ -220,14 +227,17 @@ impl GameCheetahEngine {

ui.horizontal(|ui| {
ui.spacing_mut().item_spacing = egui::Vec2::splat(5.0);
ui.label("Value:");
ui.label(fl!(crate::LANGUAGE_LOADER, "value-label"));
if let Some(search_context) = self.searches.get_mut(search_index) {
let re = ui.add(
egui::TextEdit::singleline(&mut search_context.search_value_text)
.hint_text(format!(
"Search for {} value",
search_context.search_type.get_description_text()
))
.hint_text(
fl!(
crate::LANGUAGE_LOADER,
"search-value-label",
valuetype = search_context.search_type.get_description_text()
),
)
.interactive(matches!(search_context.searching, SearchMode::None)),
);

Expand Down Expand Up @@ -274,7 +284,7 @@ impl GameCheetahEngine {
if ui
.add_enabled(
!search_context.old_results.is_empty(),
egui::Button::new("Undo"),
egui::Button::new(fl!(crate::LANGUAGE_LOADER, "undo-button")),
)
.clicked()
{
Expand Down Expand Up @@ -316,7 +326,7 @@ impl GameCheetahEngine {
.from_string(&search_context.search_value_text)
.is_err()
{
ui.label(RichText::new("Invalid number").color(Color32::from_rgb(200, 0, 0)));
ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "invalid-number-error")).color(Color32::from_rgb(200, 0, 0)));
}

if !matches!(
Expand All @@ -339,12 +349,12 @@ impl GameCheetahEngine {
{
let len = self.searches.get(search_index).unwrap().search_results;
if len <= 0 {
if ui.button("Initial search").clicked() {
if ui.button(fl!(crate::LANGUAGE_LOADER, "initial-search-button")).clicked() {
self.initial_search(search_index);
return;
}
if len == 0 {
ui.label("No results found.".to_string());
ui.label(fl!(crate::LANGUAGE_LOADER, "no-results-label"));
}
} else {
let auto_show_treshold = 20;
Expand All @@ -354,30 +364,30 @@ impl GameCheetahEngine {

ui.spacing_mut().item_spacing = egui::Vec2::splat(5.0);

if ui.button("Update").clicked() {
if ui.button(fl!(crate::LANGUAGE_LOADER, "update-button")).clicked() {
self.filter_searches(search_index);
return;
}
if ui.button("Clear").clicked() {
if ui.button(fl!(crate::LANGUAGE_LOADER, "clear-button")).clicked() {
search_context.clear_results(&self.freeze_sender);
return;
}
if len >= auto_show_treshold {
if self.show_results {
if ui.button("Hide Results").clicked() {
if ui.button(fl!(crate::LANGUAGE_LOADER, "hide-results-button")).clicked() {
self.show_results = false;
return;
}
} else if ui.button("Show Results").clicked() {
} else if ui.button(fl!(crate::LANGUAGE_LOADER, "show-results-button")).clicked() {
self.show_results = true;
return;
}
}

if len == 1 {
ui.label(format!("found {len} result."));
ui.label(fl!(crate::LANGUAGE_LOADER, "found-one-result-label"));
} else {
ui.label(format!("found {len} results."));
ui.label(fl!(crate::LANGUAGE_LOADER, "found-results-label", results=len));
}
});

Expand All @@ -400,13 +410,13 @@ impl GameCheetahEngine {
table
.header(20.0, |mut header| {
header.col(|ui| {
ui.heading("Address");
ui.heading(fl!(crate::LANGUAGE_LOADER, "address-heading"));
});
header.col(|ui| {
ui.heading("Value");
ui.heading(fl!(crate::LANGUAGE_LOADER, "value-heading"));
});
header.col(|ui| {
ui.heading("Freezed");
ui.heading(fl!(crate::LANGUAGE_LOADER, "freezed-heading"));
});
})
.body(|body| {
Expand Down Expand Up @@ -454,15 +464,13 @@ impl GameCheetahEngine {
"Error converting {:?}: {}",
result.search_type, err
);
self.error_text = format!(
"Error converting {:?}: {}",
result.search_type, err
);
self.error_text = fl!(crate::LANGUAGE_LOADER, "conversion-error", valuetype = result.search_type.get_short_description_text(), message = err);
}
}
}
} else {
ui.label("<error>");

ui.label(fl!(crate::LANGUAGE_LOADER, "generic-error-label"));
}
}
});
Expand Down Expand Up @@ -525,16 +533,13 @@ impl GameCheetahEngine {
match search_context.searching {
SearchMode::None => {}
SearchMode::Percent => {
ui.label(format!(
"Update {}/{}…",
current_bytes, search_context.total_bytes
));
ui.label(fl!(crate::LANGUAGE_LOADER, "update-numbers-progress", current=current_bytes, total=search_context.total_bytes));
}
SearchMode::Memory => {
let bb = gabi::BytesConfig::default();
let current_bytes_out = bb.bytes(current_bytes as u64);
let total_bytes_out = bb.bytes(search_context.total_bytes as u64);
ui.label(format!("Search {current_bytes_out}/{total_bytes_out}"));
let current_bytes_out = bb.bytes(current_bytes as u64).to_string();
let total_bytes_out = bb.bytes(search_context.total_bytes as u64).to_string();
ui.label(fl!(crate::LANGUAGE_LOADER, "search-memory-progress", current=current_bytes_out, total=total_bytes_out));
}
}

Expand Down
18 changes: 18 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,21 @@ impl Message {
}
}
}


use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "i18n"] // path to the compiled localization resources
struct Localizations;

use i18n_embed::{
fluent::{fluent_language_loader, FluentLanguageLoader},
DesktopLanguageRequester,
};
use once_cell::sync::Lazy;
pub static LANGUAGE_LOADER: Lazy<FluentLanguageLoader> = Lazy::new(|| {
let loader = fluent_language_loader!();
let requested_languages = DesktopLanguageRequester::requested_languages();
let _result = i18n_embed::select(&loader, &Localizations, &requested_languages);
loader
});
Loading

0 comments on commit 9f496c0

Please sign in to comment.