Skip to content

Commit

Permalink
localization (#810)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrettMayson authored Oct 20, 2024
1 parent fb0de07 commit 73292eb
Show file tree
Hide file tree
Showing 34 changed files with 15,206 additions and 153 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"libs/preprocessor",
"libs/signing",
"libs/sqf",
"libs/stringtable",
"libs/workspace",
]
resolver = "2"
Expand Down
3 changes: 2 additions & 1 deletion bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ hemtt-pbo = { path = "../libs/pbo", version = "1.0.1" }
hemtt-preprocessor = { path = "../libs/preprocessor", version = "1.0.0" }
hemtt-signing = { path = "../libs/signing", version = "1.0.0" }
hemtt-sqf = { path = "../libs/sqf", version = "1.0.0" }
hemtt-stringtable = { path = "../libs/stringtable", version = "1.0.0" }
hemtt-workspace = { path = "../libs/workspace", version = "1.0.0" }

arma3-wiki = { workspace = true }
Expand All @@ -38,6 +39,7 @@ fs_extra = "1.3.0"
git2 = { workspace = true }
glob = "0.3.1"
num_cpus = "1.16.0"
paste = { workspace = true }
rayon = "1.10.0"
regex = { workspace = true }
reqwest = { version = "0.12.8", features = ["blocking", "json"] }
Expand All @@ -62,5 +64,4 @@ enable-ansi-support = "0.2.1"
winreg = "0.52.0"

[dev-dependencies]
paste = { workspace = true }
sealed_test = "1.1.0"
152 changes: 152 additions & 0 deletions bin/src/commands/localization/coverage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use std::{collections::HashMap, io::BufReader};

use hemtt_stringtable::{Project, Totals};
use term_table::{
row::Row,
table_cell::{Alignment, TableCell},
Table, TableStyle,
};

use crate::{context::Context, report::Report, Error};

macro_rules! missing {
($totals:ident, $missing:ident, $lang:tt, $addon:expr) => {
paste::paste! {
if $totals.$lang() != $totals.total() {
$missing.entry(stringify!($lang)).or_insert_with(Vec::new).push($addon);
}
}
};
}

fn first_capital(s: &str) -> String {
let mut c = s.chars();
c.next().map_or_else(String::new, |f| {
f.to_uppercase().collect::<String>() + c.as_str()
})
}

macro_rules! row {
($table:ident, $global:ident, $missing:ident, $lang:tt) => {
if $global.$lang() != 0 {
$table.add_row(Row::new(vec![
TableCell::new(first_capital(stringify!($lang))),
TableCell::new(format!(
"{:.2}%",
f64::from($global.$lang()) / f64::from($global.total()) * 100.0
)),
TableCell::new($global.$lang()),
{
if let Some(missing) = $missing.get(stringify!($lang)) {
if missing.len() < 3 {
TableCell::new(missing.join(", "))
} else {
TableCell::new(format!("{} and {} more", missing[0], missing.len() - 1))
}
} else {
TableCell::new("")
}
},
]));
}
};
}

pub fn coverage() -> Result<Report, Error> {
let ctx = Context::new(None, crate::context::PreservePrevious::Remove, true)?;

let mut global = Totals::default();
let mut missing = HashMap::new();

for addon in ctx.addons() {
let stringtable_path = ctx
.workspace_path()
.join(addon.folder())?
.join("stringtable.xml")?;
if stringtable_path.exists()? {
let project = match Project::from_reader(BufReader::new(stringtable_path.open_file()?))
{
Ok(project) => project,
Err(e) => {
error!("Failed to read stringtable for {}", addon.folder());
error!("{:?}", e);
return Ok(Report::new());
}
};
project.packages().iter().for_each(|package| {
let totals = package.totals();
missing!(totals, missing, original, addon.folder());
missing!(totals, missing, english, addon.folder());
missing!(totals, missing, czech, addon.folder());
missing!(totals, missing, french, addon.folder());
missing!(totals, missing, spanish, addon.folder());
missing!(totals, missing, italian, addon.folder());
missing!(totals, missing, polish, addon.folder());
missing!(totals, missing, portuguese, addon.folder());
missing!(totals, missing, russian, addon.folder());
missing!(totals, missing, german, addon.folder());
missing!(totals, missing, korean, addon.folder());
missing!(totals, missing, japanese, addon.folder());
missing!(totals, missing, chinese, addon.folder());
missing!(totals, missing, chinesesimp, addon.folder());
missing!(totals, missing, turkish, addon.folder());
missing!(totals, missing, swedish, addon.folder());
missing!(totals, missing, slovak, addon.folder());
missing!(totals, missing, serbocroatian, addon.folder());
missing!(totals, missing, norwegian, addon.folder());
missing!(totals, missing, icelandic, addon.folder());
missing!(totals, missing, hungarian, addon.folder());
missing!(totals, missing, greek, addon.folder());
missing!(totals, missing, finnish, addon.folder());
missing!(totals, missing, dutch, addon.folder());
global.merge(&totals);
});
}
}

let mut table = Table::new();
table.style = TableStyle::thin();
table.add_row(Row::new(vec![
TableCell::builder("Language")
.alignment(Alignment::Center)
.build(),
TableCell::builder("Percent")
.alignment(Alignment::Center)
.build(),
TableCell::builder("Total")
.alignment(Alignment::Center)
.build(),
TableCell::builder("Missing Addons")
.alignment(Alignment::Center)
.build(),
]));

row!(table, global, missing, original);
row!(table, global, missing, english);
row!(table, global, missing, czech);
row!(table, global, missing, french);
row!(table, global, missing, spanish);
row!(table, global, missing, italian);
row!(table, global, missing, polish);
row!(table, global, missing, portuguese);
row!(table, global, missing, russian);
row!(table, global, missing, german);
row!(table, global, missing, korean);
row!(table, global, missing, japanese);
row!(table, global, missing, chinese);
row!(table, global, missing, chinesesimp);
row!(table, global, missing, turkish);
row!(table, global, missing, swedish);
row!(table, global, missing, slovak);
row!(table, global, missing, serbocroatian);
row!(table, global, missing, norwegian);
row!(table, global, missing, icelandic);
row!(table, global, missing, hungarian);
row!(table, global, missing, greek);
row!(table, global, missing, finnish);
row!(table, global, missing, dutch);

println!("{}", table.render());

Ok(Report::new())
}
33 changes: 33 additions & 0 deletions bin/src/commands/localization/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use clap::{ArgMatches, Command};

use crate::{report::Report, Error};

mod coverage;
mod sort;

#[must_use]
pub fn cli() -> Command {
Command::new("localization")
.visible_alias("ln")
.about("Manage localization stringtables")
.subcommand(Command::new("coverage").about("Check the coverage of localization"))
.subcommand(Command::new("sort").about("Sort the stringtables"))
}

/// Execute the localization command
///
/// # Errors
/// [`Error`] depending on the modules
///
/// # Panics
/// If a name is not provided, but this is usually handled by clap
pub fn execute(matches: &ArgMatches) -> Result<Report, Error> {
match matches.subcommand() {
Some(("coverage", _)) => coverage::coverage(),
Some(("sort", _)) => sort::sort(),
_ => {
cli().print_help().expect("Failed to print help");
Ok(Report::new())
}
}
}
44 changes: 44 additions & 0 deletions bin/src/commands/localization/sort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::io::BufReader;

use hemtt_stringtable::Project;

use crate::{context::Context, report::Report, Error};

pub fn sort() -> Result<Report, Error> {
let ctx = Context::new(None, crate::context::PreservePrevious::Remove, true)?;

for addon in ctx.addons() {
let stringtable_path = ctx
.workspace_path()
.join(addon.folder())?
.join("stringtable.xml")?;
if stringtable_path.exists()? {
match Project::from_reader(BufReader::new(stringtable_path.open_file()?)) {
Ok(mut project) => {
project.sort();
let out_path = ctx
.project_folder()
.join(addon.folder_pathbuf())
.join("stringtable.xml");
let mut writer = String::new();
if let Err(e) = project.to_writer(&mut writer) {
error!("Failed to write stringtable for {}", addon.folder());
error!("{:?}", e);
return Ok(Report::new());
}
if let Err(e) = std::fs::write(out_path, writer) {
error!("Failed to write stringtable for {}", addon.folder());
error!("{:?}", e);
return Ok(Report::new());
}
}
Err(e) => {
error!("Failed to read stringtable for {}", addon.folder());
error!("{:?}", e);
return Ok(Report::new());
}
};
}
}
Ok(Report::new())
}
1 change: 1 addition & 0 deletions bin/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod build;
pub mod check;
pub mod dev;
pub mod launch;
pub mod localization;
pub mod new;
pub mod release;
pub mod script;
Expand Down
2 changes: 2 additions & 0 deletions bin/src/commands/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub fn cli() -> Command {
.about("Use HEMTT standalone utils")
.subcommand_required(false)
.arg_required_else_help(true)
.subcommand(utils::config::cli())
.subcommand(utils::inspect::cli())
.subcommand(utils::paa::cli())
.subcommand(utils::pbo::cli())
Expand All @@ -21,6 +22,7 @@ pub fn cli() -> Command {
/// [`Error`] depending on the modules
pub fn execute(matches: &ArgMatches) -> Result<Report, Error> {
match matches.subcommand() {
Some(("config", matches)) => utils::config::execute(matches),
Some(("inspect", matches)) => utils::inspect::execute(matches),
Some(("paa", matches)) => utils::paa::execute(matches),
Some(("pbo", matches)) => utils::pbo::execute(matches),
Expand Down
11 changes: 7 additions & 4 deletions bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub fn cli() -> Command {
.subcommand(commands::book::cli())
.subcommand(commands::new::cli())
.subcommand(commands::check::cli())
.subcommand(commands::localization::cli())
.subcommand(commands::dev::cli())
.subcommand(commands::launch::cli())
.subcommand(commands::build::cli())
Expand Down Expand Up @@ -138,6 +139,9 @@ pub fn execute(matches: &ArgMatches) -> Result<(), Error> {
Some(("new", matches)) => commands::new::execute(matches).map(Some),
Some(("dev", matches)) => commands::dev::execute(matches, &[]).map(Some),
Some(("check", _matches)) => commands::check::execute().map(Some),
Some(("localization", matches)) => commands::localization::execute(matches)
.map_err(std::convert::Into::into)
.map(Some),
Some(("build", matches)) => commands::build::execute(matches)
.map_err(std::convert::Into::into)
.map(Some),
Expand Down Expand Up @@ -166,10 +170,9 @@ pub fn execute(matches: &ArgMatches) -> Result<(), Error> {
};
if let Some(report) = report? {
report.write_to_stdout();
if !matches
.subcommand_name()
.is_some_and(|s| s == "new" || s == "utils" || s == "wiki" || s == "book")
{
if !matches.subcommand_name().is_some_and(|s| {
s == "new" || s == "utils" || s == "wiki" || s == "book" || s == "localization"
}) {
report.write_ci_annotations()?;
}
if report.failed() {
Expand Down
Loading

0 comments on commit 73292eb

Please sign in to comment.