Skip to content
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

Integrate to the bootstrap process of rust-lang/rust #193

Merged
merged 1 commit into from
May 21, 2024
Merged
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
2 changes: 1 addition & 1 deletion i18n-helpers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mdbook.workspace = true
polib.workspace = true
pulldown-cmark = { version = "0.10.3", default-features = false, features = ["html"] }
pulldown-cmark-to-cmark = "13.0.0"
regex = "1.10.4"
regex = "1.9"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might get a PR from the dependabot, but we can then tell it to ignore the 1.10 releases.

semver = "1.0.23"
serde_json.workspace = true
syntect = "5.2.0"
Expand Down
72 changes: 12 additions & 60 deletions i18n-helpers/src/bin/mdbook-gettext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,63 +24,14 @@
//! PO files. If the PO file is not found, you'll get the untranslated
//! book.

use anyhow::{anyhow, Context};
use mdbook::preprocess::{CmdPreprocessor, PreprocessorContext};
use mdbook_i18n_helpers::gettext::{add_stripped_summary_translations, translate_book};
use polib::catalog::Catalog;
use polib::po_file;
use mdbook::preprocess::{CmdPreprocessor, Preprocessor};
use mdbook_i18n_helpers::preprocessors::Gettext;
use semver::{Version, VersionReq};
use std::path::PathBuf;
use std::{io, process};

/// Check whether the book should be transalted.
///
/// The book should be translated if:
/// * `book.language` is defined in mdbook config
/// * Corresponding {language}.po defined
fn should_translate(ctx: &PreprocessorContext) -> bool {
// Translation is a no-op when the target language is not set
if ctx.config.book.language.is_none() {
return false;
}

// Nothing to do if PO file is missing.
get_catalog_path(ctx)
.map(|path| path.try_exists().unwrap_or(false))
.unwrap_or(false)
}

/// Compute the path of the Catalog file.
fn get_catalog_path(ctx: &PreprocessorContext) -> anyhow::Result<PathBuf> {
let language = ctx
.config
.book
.language
.as_ref()
.ok_or_else(|| anyhow!("Language is not provided"))?;

let cfg = ctx
.config
.get_preprocessor("gettext")
.ok_or_else(|| anyhow!("Could not read preprocessor.gettext configuration"))?;
let po_dir = cfg.get("po-dir").and_then(|v| v.as_str()).unwrap_or("po");
Ok(ctx.root.join(po_dir).join(format!("{language}.po")))
}

/// Load the catalog with translation strings.
fn load_catalog(ctx: &PreprocessorContext) -> anyhow::Result<Catalog> {
let path = get_catalog_path(ctx)?;

let catalog = po_file::parse(&path)
.map_err(|err| anyhow!("{err}"))
.with_context(|| format!("Could not parse {path:?} as PO file"))?;

Ok(catalog)
}

/// Execute main logic by this mdbook preprocessor.
fn preprocess() -> anyhow::Result<()> {
let (ctx, mut book) = CmdPreprocessor::parse_input(io::stdin())?;
let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
let book_version = Version::parse(&ctx.mdbook_version)?;
let version_req = VersionReq::parse(mdbook::MDBOOK_VERSION)?;
#[allow(clippy::print_stderr)]
Expand All @@ -93,11 +44,8 @@ fn preprocess() -> anyhow::Result<()> {
);
}

if should_translate(&ctx) {
let mut catalog = load_catalog(&ctx)?;
add_stripped_summary_translations(&mut catalog);
translate_book(&catalog, &mut book);
}
let gettext = Gettext;
let book = gettext.run(&ctx, book)?;
Comment on lines +47 to +48
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like above, I think we don't need the variable:

Suggested change
let gettext = Gettext;
let book = gettext.run(&ctx, book)?;
let book = Gettext::run(&ctx, book)?;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @dalance, can you try out this change and see if it compiles? If so, please add it to the PR. Thanks!


serde_json::to_writer(io::stdout(), &book)?;

Expand All @@ -107,10 +55,14 @@ fn preprocess() -> anyhow::Result<()> {
fn main() -> anyhow::Result<()> {
if std::env::args().len() == 3 {
assert_eq!(std::env::args().nth(1).as_deref(), Some("supports"));
if let Some("xgettext") = std::env::args().nth(2).as_deref() {
process::exit(1)
if let Some(renderer) = std::env::args().nth(2).as_deref() {
let gettext = Gettext;
if gettext.supports_renderer(renderer) {
Comment on lines +59 to +60
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work as well:

Suggested change
let gettext = Gettext;
if gettext.supports_renderer(renderer) {
if Gettext::supports_renderer(renderer) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't change the definition of supports_renderer and run because they are defined as methods of Preprocessor trait at https://docs.rs/mdbook/latest/mdbook/preprocess/trait.Preprocessor.html.

I can define another static method like impl_supports_renderer, and change it to Gettext::impl_supports_renderer,
but it may be redundant.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now! The syntax I used above would only work if the supports_renderer method had no &self argument.

Then let's merge this PR as it is — and then later change the code here to create a single Gettext value and pass this around. That would be a little more natural, I think (thought it would just be a small refactor).

process::exit(0)
} else {
process::exit(1)
}
} else {
// Signal that we support all other renderers.
process::exit(0);
}
}
Expand Down
9 changes: 5 additions & 4 deletions i18n-helpers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use syntect::parsing::{ParseState, Scope, ScopeStack, SyntaxSet};
pub mod directives;
pub mod gettext;
pub mod normalize;
pub mod preprocessors;
pub mod xgettext;

/// Re-wrap the sources field of a message.
Expand Down Expand Up @@ -93,7 +94,7 @@ pub fn new_cmark_parser<'input, F: BrokenLinkCallback<'input>>(
/// ```
pub fn extract_events<'a>(text: &'a str, state: Option<State<'a>>) -> Vec<(usize, Event<'a>)> {
// Expand a `[foo]` style link into `[foo][foo]`.
fn expand_shortcut_link(tag: Tag) -> Tag {
fn expand_shortcut_link(tag: Tag<'_>) -> Tag<'_> {
match tag {
Tag::Link {
link_type: LinkType::Shortcut,
Expand Down Expand Up @@ -406,7 +407,7 @@ pub fn group_events<'a>(events: &'a [(usize, Event<'a>)]) -> Vec<Group<'a>> {
}

/// Returns true if the events appear to be a codeblock.
fn is_codeblock_group(events: &[(usize, Event)]) -> bool {
fn is_codeblock_group(events: &[(usize, Event<'_>)]) -> bool {
matches!(
events,
[
Expand All @@ -429,7 +430,7 @@ fn is_translate_scope(x: Scope) -> bool {

/// Creates groups by checking codeblock with heuristic way.
fn heuristic_codeblock<'a>(
events: &'a [(usize, Event)],
events: &'a [(usize, Event<'_>)],
mut ctx: GroupingContext,
) -> (Vec<Group<'a>>, GroupingContext) {
let is_translate = match events {
Expand Down Expand Up @@ -458,7 +459,7 @@ fn heuristic_codeblock<'a>(

/// Creates groups by parsing codeblock.
fn parse_codeblock<'a>(
events: &'a [(usize, Event)],
events: &'a [(usize, Event<'_>)],
mut ctx: GroupingContext,
) -> (Vec<Group<'a>>, GroupingContext) {
// Language detection from language identifier of codeblock.
Expand Down
2 changes: 2 additions & 0 deletions i18n-helpers/src/preprocessors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod gettext;
pub use gettext::Gettext;
91 changes: 91 additions & 0 deletions i18n-helpers/src/preprocessors/gettext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::gettext::{add_stripped_summary_translations, translate_book};
use anyhow::{anyhow, Context};
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use polib::catalog::Catalog;
use polib::po_file;
use std::path::PathBuf;

/// Check whether the book should be transalted.
///
/// The book should be translated if:
/// * `book.language` is defined in mdbook config
/// * Corresponding {language}.po defined
fn should_translate(ctx: &PreprocessorContext) -> bool {
// Translation is a no-op when the target language is not set
if ctx.config.book.language.is_none() {
return false;
}

// Nothing to do if PO file is missing.
get_catalog_path(ctx)
.map(|path| path.try_exists().unwrap_or(false))
.unwrap_or(false)
}

/// Compute the path of the Catalog file.
fn get_catalog_path(ctx: &PreprocessorContext) -> anyhow::Result<PathBuf> {
let language = ctx
.config
.book
.language
.as_ref()
.ok_or_else(|| anyhow!("Language is not provided"))?;

let cfg = ctx
.config
.get_preprocessor("gettext")
.ok_or_else(|| anyhow!("Could not read preprocessor.gettext configuration"))?;
let po_dir = cfg.get("po-dir").and_then(|v| v.as_str()).unwrap_or("po");
Ok(ctx.root.join(po_dir).join(format!("{language}.po")))
}

/// Load the catalog with translation strings.
fn load_catalog(ctx: &PreprocessorContext) -> anyhow::Result<Catalog> {
let path = get_catalog_path(ctx)?;

let catalog = po_file::parse(&path)
.map_err(|err| anyhow!("{err}"))
.with_context(|| format!("Could not parse {path:?} as PO file"))?;

Ok(catalog)
}

/// Preprocessor for gettext
pub struct Gettext;

impl Preprocessor for Gettext {
fn name(&self) -> &str {
"gettext"
}

fn run(
&self,
ctx: &PreprocessorContext,
mut book: mdbook::book::Book,
) -> anyhow::Result<mdbook::book::Book> {
if should_translate(ctx) {
let mut catalog = load_catalog(ctx)?;
add_stripped_summary_translations(&mut catalog);
translate_book(&catalog, &mut book);
}
Ok(book)
}

fn supports_renderer(&self, renderer: &str) -> bool {
renderer != "xgettext"
}
}