Skip to content

Commit

Permalink
Add gettext command to generate translated output
Browse files Browse the repository at this point in the history
This command is the second part of a Gettext-based translation (i18n)
workflow. It takes an `xx.po` file with translations and uses this to
translate the chapters of the book. Paragraphs without a translation
are kept in the original language.

Part of the solution for #5.
  • Loading branch information
mgeisler committed Sep 8, 2022
1 parent 8820db4 commit 11d39d8
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 0 deletions.
82 changes: 82 additions & 0 deletions src/cmd/gettext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::cmd::xgettext::extract_paragraphs;
use crate::get_book_dir;
use crate::utils;
use clap::{arg, App, Arg, ArgMatches};
use mdbook::MDBook;
use polib::po_file::parse;
use std::path::Path;

// Create clap subcommand arguments
pub fn make_subcommand<'help>() -> App<'help> {
App::new("gettext")
.about("Output translated book")
.arg(
Arg::new("dest-dir")
.short('d')
.long("dest-dir")
.value_name("dest-dir")
.help(
"Output directory for the translated book{n}\
Relative paths are interpreted relative to the book's root directory{n}\
If omitted, mdBook defaults to `./src/xx` where `xx` is the language of the PO file."
),
)
.arg(arg!(<po> "PO file to generate translation for"))
.arg(arg!([dir]
"Root directory for the book{n}\
(Defaults to the Current Directory when omitted)"
))
}

// Gettext command implementation
pub fn execute(args: &ArgMatches) -> mdbook::errors::Result<()> {
let book_dir = get_book_dir(args);
let book = MDBook::load(&book_dir)?;

let po_file = Path::new(args.value_of("po").unwrap());
let lang = po_file.file_stem().unwrap();
let catalog = parse(po_file).unwrap();
let dest_dir = book.root.join(
args.value_of("dest-dir")
.map(|path| path.into())
.unwrap_or(Path::new("src").join(lang)),
);

for item in book.iter() {
match item {
mdbook::BookItem::Chapter(chapter) if !chapter.is_draft_chapter() => {
let mut output = String::with_capacity(chapter.content.len());
let mut current_lineno = 1;

for (lineno, paragraph) in extract_paragraphs(&chapter.content) {
// Fill in blank lines between paragraphs. This is
// important for code blocks where blank lines can
// be significant.
while current_lineno < lineno {
output.push('\n');
current_lineno += 1;
}
current_lineno += paragraph.lines().count();

let translated = catalog
.find_message(paragraph)
.and_then(|msg| msg.get_msgstr().ok())
.filter(|msgstr| !msgstr.is_empty())
.map(|msgstr| msgstr.as_str())
.unwrap_or(paragraph);
output.push_str(translated);
output.push('\n');
}

utils::fs::write_file(
&dest_dir,
chapter.source_path.as_ref().unwrap(),
output.as_bytes(),
)?;
}
_ => {}
}
}

Ok(())
}
1 change: 1 addition & 0 deletions src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ pub mod test;
#[cfg(feature = "watch")]
pub mod watch;
pub mod xgettext;
pub mod gettext;
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fn main() {
Some(("serve", sub_matches)) => cmd::serve::execute(sub_matches),
Some(("test", sub_matches)) => cmd::test::execute(sub_matches),
Some(("xgettext", sub_matches)) => cmd::xgettext::execute(sub_matches),
Some(("gettext", sub_matches)) => cmd::gettext::execute(sub_matches),
Some(("completions", sub_matches)) => (|| {
let shell: Shell = sub_matches
.value_of("shell")
Expand Down Expand Up @@ -78,6 +79,7 @@ fn create_clap_app() -> App<'static> {
.subcommand(cmd::test::make_subcommand())
.subcommand(cmd::clean::make_subcommand())
.subcommand(cmd::xgettext::make_subcommand())
.subcommand(cmd::gettext::make_subcommand())
.subcommand(
App::new("completions")
.about("Generate shell completions for your shell to stdout")
Expand Down

0 comments on commit 11d39d8

Please sign in to comment.