Skip to content

xgettext: Rough draft v2 #403

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@

target/

locale/posixutils-rs.pot
locale/*/LC_MESSAGES/posixutils-rs.mo
21 changes: 21 additions & 0 deletions Cargo.lock

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

20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: MIT

PROJECT_NAME = posixutils-rs

SRCS := $(shell find . -name '*.rs' -not -path './target/*' -not -path './*/tests/*')
POS := $(shell find locale -name '*.po')
MOS := $(POS:.po=.mo)

locale: $(MOS)

%.mo: %.po locale/${PROJECT_NAME}.pot
msgmerge $^ -o $<
msgfmt $< -o $@

locale/${PROJECT_NAME}.pot: $(SRCS)
cargo r --release --bin xgettext -- -n -p locale -d ${PROJECT_NAME} $^

clean:
rm -rf locale/${PROJECT_NAME}.pot
rm -rf $(MOS)
11 changes: 11 additions & 0 deletions i18n/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ bytemuck = { version = "1.17", features = ["derive"] }
byteorder = "1.5"
strum = "0.26"
strum_macros = "0.26"
proc-macro2 = { version = "1", features = ["span-locations"] }
quote = "1"
syn = { version = "2", features = ["parsing", "full"] }

[dev-dependencies]
tempfile = "3.10"
pretty_assertions = "1.4"

[lints]
workspace = true
Expand All @@ -26,3 +33,7 @@ path = "./gencat.rs"
[[bin]]
name = "iconv"
path = "./iconv.rs"

[[bin]]
name = "xgettext"
path = "./xgettext.rs"
2 changes: 1 addition & 1 deletion i18n/iconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use strum_macros::{Display, EnumIter, EnumString};
mod iconv_lib;

#[derive(Parser)]
#[command(version, about=gettext("iconv codeset conversion"))]
#[command(version, about=gettext("iconv - codeset conversion"))]
struct Args {
#[arg(short = 'c', help=gettext("Omit invalid characters of the input file from the output"))]
omit_invalid: bool,
Expand Down
1 change: 1 addition & 0 deletions i18n/tests/i18n-tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@

mod gencat;
mod iconv;
mod xgettext;
171 changes: 171 additions & 0 deletions i18n/tests/xgettext/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: MIT

use std::fs::{read_to_string, remove_file};
use std::path::Path;

use pretty_assertions::assert_eq;
use tempfile::tempdir;

use plib::testing::{run_test, TestPlan};

fn xgettext_test<P: AsRef<Path>, P2: AsRef<Path>>(
args: &[&str],
output_file: P,
expected_output_file: P2,
) {
let str_args: Vec<String> = args.iter().map(|s| String::from(*s)).collect();
run_test(TestPlan {
cmd: String::from("xgettext"),
args: str_args,
stdin_data: "".into(),
expected_out: "".into(),
expected_err: "".into(),
expected_exit_code: 0,
});

let output = read_to_string(output_file).expect("Unable to open po-file");
let expected_out = read_to_string(expected_output_file).unwrap();
assert_eq!(output, expected_out);
}

#[test]
fn test_xgettext_no_arg() {
run_test(TestPlan {
cmd: String::from("xgettext"),
args: vec![],
stdin_data: "".into(),
expected_out: "".into(),
expected_err: "xgettext: no input file given\n".into(),
expected_exit_code: 1,
});
}

#[test]
fn test_xgettext() {
let output_file = "messages.pot";
xgettext_test(
&["tests/xgettext/test_gettext.rs"],
output_file,
"tests/xgettext/test_gettext_no_lines.pot",
);
let _ = remove_file(output_file);
}

#[test]
fn test_xgettext_domain() {
let output_file = "domain.pot";
xgettext_test(
&["-d", "domain", "tests/xgettext/test_gettext.rs"],
output_file,
"tests/xgettext/test_gettext_no_lines.pot",
);
let _ = remove_file(output_file);
}

#[test]
fn test_xgettext_pathname() {
let temp_dir = tempdir().expect("Unable to create temporary directory");
xgettext_test(
&[
"-p",
&temp_dir.path().to_str().unwrap(),
"tests/xgettext/test_gettext.rs",
],
temp_dir.path().join("messages.pot"),
"tests/xgettext/test_gettext_no_lines.pot",
);
}

#[test]
fn test_xgettext_domain_pathname() {
let temp_dir = tempdir().expect("Unable to create temporary directory");
xgettext_test(
&[
"-d",
"domain",
"-p",
&temp_dir.path().to_str().unwrap(),
"tests/xgettext/test_gettext.rs",
],
temp_dir.path().join("domain.pot"),
"tests/xgettext/test_gettext_no_lines.pot",
);
}

#[test]
fn test_xgettext_pathname_lines() {
let temp_dir = tempdir().expect("Unable to create temporary directory");
xgettext_test(
&[
"-n",
"-p",
&temp_dir.path().to_str().unwrap(),
"tests/xgettext/test_gettext.rs",
],
temp_dir.path().join("messages.pot"),
"tests/xgettext/test_gettext.pot",
);
}

#[test]
fn test_clap() {
let temp_dir = tempdir().expect("Unable to create temporary directory");
xgettext_test(
&[
"-n",
"-p",
&temp_dir.path().to_str().unwrap(),
"tests/xgettext/test_clap.rs",
],
temp_dir.path().join("messages.pot"),
"tests/xgettext/test_clap.pot",
);
}

#[ignore]
#[test]
fn test_xgettext_ngettext() {
let temp_dir = tempdir().expect("Unable to create temporary directory");
xgettext_test(
&[
"-n",
"-p",
&temp_dir.path().to_str().unwrap(),
"tests/xgettext/test_ngettext.rs",
],
temp_dir.path().join("messages.pot"),
"tests/xgettext/test_ngettext.pot",
);
}

#[ignore]
#[test]
fn test_xgettext_pgettext() {
let temp_dir = tempdir().expect("Unable to create temporary directory");
xgettext_test(
&[
"-n",
"-p",
&temp_dir.path().to_str().unwrap(),
"tests/xgettext/test_pgettext.rs",
],
temp_dir.path().join("messages.pot"),
"tests/xgettext/test_pgettext.pot",
);
}

#[ignore]
#[test]
fn test_xgettext_npgettext() {
let temp_dir = tempdir().expect("Unable to create temporary directory");
xgettext_test(
&[
"-n",
"-p",
&temp_dir.path().to_str().unwrap(),
"tests/xgettext/test_npgettext.rs",
],
temp_dir.path().join("messages.pot"),
"tests/xgettext/test_npgettext.pot",
);
}
36 changes: 36 additions & 0 deletions i18n/tests/xgettext/test_clap.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#: tests/xgettext/test_clap.rs:29
msgid "Arguments for the utility"
msgstr ""

#: tests/xgettext/test_clap.rs:33
msgid "Print help"
msgstr ""

#: tests/xgettext/test_clap.rs:36
msgid "Print version"
msgstr ""

#: tests/xgettext/test_clap.rs:23
msgid "The utility to be invoked"
msgstr ""

#: tests/xgettext/test_clap.rs:19
msgid "Write timing output to standard error in POSIX format"
msgstr ""

#: tests/xgettext/test_clap.rs:10
msgid "time - time a simple command or give resource usage"
msgstr ""

#: tests/xgettext/test_clap.rs:11
msgid "{about}\n"
"\n"
"Usage: {usage}\n"
"\n"
"Arguments:\n"
"{positionals}\n"
"\n"
"Options:\n"
"{options}"
msgstr ""

38 changes: 38 additions & 0 deletions i18n/tests/xgettext/test_clap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use clap::Parser;

use gettextrs::{
bind_textdomain_codeset, bindtextdomain, gettext, setlocale, textdomain, LocaleCategory,
};

#[derive(Parser)]
#[command(
version,
about = gettext("time - time a simple command or give resource usage"),
help_template = gettext("{about}\n\nUsage: {usage}\n\nArguments:\n{positionals}\n\nOptions:\n{options}"),
disable_help_flag = true,
disable_version_flag = true,
)]
struct Args {
#[arg(
short,
long,
help = gettext("Write timing output to standard error in POSIX format")
)]
posix: bool,

#[arg(help = gettext("The utility to be invoked"))]
utility: String,

#[arg(
name = "ARGUMENT",
trailing_var_arg = true,
help = gettext("Arguments for the utility")
)]
arguments: Vec<String>,

#[arg(short, long, help = gettext("Print help"), action = clap::ArgAction::HelpLong)]
help: Option<bool>,

#[arg(short = 'V', long, help = gettext("Print version"), action = clap::ArgAction::Version)]
version: Option<bool>,
}
4 changes: 4 additions & 0 deletions i18n/tests/xgettext/test_gettext.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#: tests/xgettext/test_gettext.rs:6
msgid "Hello, world!"
msgstr ""

9 changes: 9 additions & 0 deletions i18n/tests/xgettext/test_gettext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use gettextrs::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
// `gettext()` simultaneously marks a string for translation and translates
// it at runtime.
println!("Translated: {}", gettext("Hello, world!"));

Ok(())
}
3 changes: 3 additions & 0 deletions i18n/tests/xgettext/test_gettext_no_lines.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
msgid "Hello, world!"
msgstr ""

6 changes: 6 additions & 0 deletions i18n/tests/xgettext/test_ngettext.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#: tests/xgettext/test_ngettext.rs:7
#: tests/xgettext/test_ngettext.rs:8
msgid "One thing"
msgid_plural "Multiple things"
msgstr[0] ""
msgstr[1] ""
11 changes: 11 additions & 0 deletions i18n/tests/xgettext/test_ngettext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use gettextrs::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
// gettext supports plurals, i.e. you can have different messages depending
// on the number of items the message mentions. This even works for
// languages that have more than one plural form, like Russian or Czech.
println!("Singular: {}", ngettext("One thing", "Multiple things", 1));
println!("Plural: {}", ngettext("One thing", "Multiple things", 2));

Ok(())
}
Loading