Move i18n helpers into ftl/, with a single main.rs
Clap gives us a nice help message and better arg parsing
This commit is contained in:
parent
78b4a391cc
commit
13572a86b2
|
@ -190,17 +190,6 @@ dependencies = [
|
||||||
"unic-langid",
|
"unic-langid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anki_i18n_helpers"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"fluent-syntax",
|
|
||||||
"lazy_static",
|
|
||||||
"regex",
|
|
||||||
"serde_json",
|
|
||||||
"walkdir",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anki_io"
|
name = "anki_io"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -1295,8 +1284,17 @@ dependencies = [
|
||||||
name = "ftl"
|
name = "ftl"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anki_io",
|
||||||
|
"anki_process",
|
||||||
|
"anyhow",
|
||||||
"camino",
|
"camino",
|
||||||
|
"clap",
|
||||||
|
"fluent-syntax",
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
"serde_json",
|
||||||
"snafu",
|
"snafu",
|
||||||
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -9,7 +9,6 @@ edition = "2021"
|
||||||
members = [
|
members = [
|
||||||
"rslib",
|
"rslib",
|
||||||
"rslib/i18n",
|
"rslib/i18n",
|
||||||
"rslib/i18n_helpers",
|
|
||||||
"rslib/linkchecker",
|
"rslib/linkchecker",
|
||||||
"rslib/proto",
|
"rslib/proto",
|
||||||
"rslib/io",
|
"rslib/io",
|
||||||
|
|
|
@ -8,11 +8,11 @@ use ninja_gen::build::FilesHandle;
|
||||||
use ninja_gen::cargo::CargoBuild;
|
use ninja_gen::cargo::CargoBuild;
|
||||||
use ninja_gen::cargo::CargoClippy;
|
use ninja_gen::cargo::CargoClippy;
|
||||||
use ninja_gen::cargo::CargoFormat;
|
use ninja_gen::cargo::CargoFormat;
|
||||||
use ninja_gen::cargo::CargoRun;
|
|
||||||
use ninja_gen::cargo::CargoTest;
|
use ninja_gen::cargo::CargoTest;
|
||||||
use ninja_gen::cargo::RustOutput;
|
use ninja_gen::cargo::RustOutput;
|
||||||
use ninja_gen::git::SyncSubmodule;
|
use ninja_gen::git::SyncSubmodule;
|
||||||
use ninja_gen::glob;
|
use ninja_gen::glob;
|
||||||
|
use ninja_gen::hash::simple_hash;
|
||||||
use ninja_gen::input::BuildInput;
|
use ninja_gen::input::BuildInput;
|
||||||
use ninja_gen::inputs;
|
use ninja_gen::inputs;
|
||||||
use ninja_gen::Build;
|
use ninja_gen::Build;
|
||||||
|
@ -60,21 +60,30 @@ fn prepare_translations(build: &mut Build) -> Result<()> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
build.add_action(
|
build.add_action(
|
||||||
"ftl:sync",
|
"ftl:bin",
|
||||||
CargoRun {
|
CargoBuild {
|
||||||
binary_name: "ftl-sync",
|
inputs: inputs![glob!["ftl/**"],],
|
||||||
cargo_args: "-p ftl",
|
outputs: &[RustOutput::Binary("ftl")],
|
||||||
bin_args: "",
|
target: None,
|
||||||
deps: inputs![":ftl:repo", glob!["ftl/{core,core-repo,qt,qt-repo}/**"]],
|
extra_args: "-p ftl",
|
||||||
|
release_override: None,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// These don't use :group notation, as it doesn't make sense to invoke multiple
|
||||||
|
// commands as a group.
|
||||||
|
build.add_action(
|
||||||
|
"ftl-sync",
|
||||||
|
FtlCommand {
|
||||||
|
args: "sync",
|
||||||
|
deps: inputs![":ftl:repo", glob!["ftl/**"]],
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
build.add_action(
|
build.add_action(
|
||||||
"ftl:deprecate",
|
"ftl-deprecate",
|
||||||
CargoRun {
|
FtlCommand {
|
||||||
binary_name: "deprecate_ftl_entries",
|
args: "deprecate --ftl-roots ftl/core ftl/qt --source-roots pylib qt rslib ts --json-roots ftl/usage",
|
||||||
cargo_args: "-p anki_i18n_helpers",
|
|
||||||
bin_args: "ftl/core ftl/qt -- pylib qt rslib ts --keep ftl/usage",
|
|
||||||
deps: inputs!["ftl/core", "ftl/qt", "pylib", "qt", "rslib", "ts"],
|
deps: inputs!["ftl/core", "ftl/qt", "pylib", "qt", "rslib", "ts"],
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -82,6 +91,24 @@ fn prepare_translations(build: &mut Build) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FtlCommand {
|
||||||
|
args: &'static str,
|
||||||
|
deps: BuildInput,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuildAction for FtlCommand {
|
||||||
|
fn command(&self) -> &str {
|
||||||
|
"$ftl_bin $args"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn files(&mut self, build: &mut impl FilesHandle) {
|
||||||
|
build.add_inputs("", &self.deps);
|
||||||
|
build.add_inputs("ftl_bin", inputs![":ftl:bin"]);
|
||||||
|
build.add_variable("args", self.args);
|
||||||
|
build.add_output_stamp(format!("ftl/stamp.{}", simple_hash(self.args)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_proto_descriptors_and_interfaces(build: &mut Build) -> Result<()> {
|
fn build_proto_descriptors_and_interfaces(build: &mut Build) -> Result<()> {
|
||||||
let outputs = vec![
|
let outputs = vec![
|
||||||
RustOutput::Data("descriptors.bin", "rslib/proto/descriptors.bin"),
|
RustOutput::Data("descriptors.bin", "rslib/proto/descriptors.bin"),
|
||||||
|
|
|
@ -6,11 +6,17 @@ edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
publish = false
|
publish = false
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
description = "Helpers for Anki's i18n system"
|
||||||
[[bin]]
|
|
||||||
name = "ftl-sync"
|
|
||||||
path = "sync.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anki_io.workspace = true
|
||||||
|
anki_process.workspace = true
|
||||||
|
anyhow.workspace = true
|
||||||
camino.workspace = true
|
camino.workspace = true
|
||||||
|
clap.workspace = true
|
||||||
|
fluent-syntax.workspace = true
|
||||||
|
lazy_static.workspace = true
|
||||||
|
regex.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
|
walkdir.workspace = true
|
||||||
|
|
|
@ -5,7 +5,12 @@ use std::collections::HashSet;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anki_io::create_file;
|
||||||
|
use anyhow::Context;
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Args;
|
||||||
use fluent_syntax::ast;
|
use fluent_syntax::ast;
|
||||||
use fluent_syntax::ast::Resource;
|
use fluent_syntax::ast::Resource;
|
||||||
use fluent_syntax::parser;
|
use fluent_syntax::parser;
|
||||||
|
@ -17,42 +22,61 @@ use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::serialize;
|
use crate::serialize;
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct WriteJsonArgs {
|
||||||
|
target_filename: PathBuf,
|
||||||
|
source_roots: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct GarbageCollectArgs {
|
||||||
|
json_root: String,
|
||||||
|
ftl_roots: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct DeprecateEntriesArgs {
|
||||||
|
#[clap(long, num_args(1..), required(true))]
|
||||||
|
ftl_roots: Vec<String>,
|
||||||
|
#[clap(long, num_args(1..), required(true))]
|
||||||
|
source_roots: Vec<String>,
|
||||||
|
#[clap(long, num_args(1..), required(true))]
|
||||||
|
json_roots: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
const DEPCRATION_WARNING: &str =
|
const DEPCRATION_WARNING: &str =
|
||||||
"NO NEED TO TRANSLATE. This text is no longer used by Anki, and will be removed in the future.";
|
"NO NEED TO TRANSLATE. This text is no longer used by Anki, and will be removed in the future.";
|
||||||
|
|
||||||
/// Extract references from all Rust, Python, TS, Svelte, Swift, Kotlin and
|
/// Extract references from all Rust, Python, TS, Svelte, Swift, Kotlin and
|
||||||
/// Designer files in the `roots`, convert them to kebab case and write them as
|
/// Designer files in the `roots`, convert them to kebab case and write them as
|
||||||
/// a json to the target file.
|
/// a json to the target file.
|
||||||
pub fn write_ftl_json<S1: AsRef<str>, S2: AsRef<str>>(roots: &[S1], target: S2) {
|
pub fn write_ftl_json(args: WriteJsonArgs) -> Result<()> {
|
||||||
let refs = gather_ftl_references(roots);
|
let refs = gather_ftl_references(&args.source_roots);
|
||||||
let mut refs = Vec::from_iter(refs);
|
let mut refs = Vec::from_iter(refs);
|
||||||
refs.sort();
|
refs.sort();
|
||||||
serde_json::to_writer_pretty(
|
serde_json::to_writer_pretty(create_file(args.target_filename)?, &refs)
|
||||||
fs::File::create(target.as_ref()).expect("failed to create file"),
|
.context("writing json")?;
|
||||||
&refs,
|
|
||||||
)
|
Ok(())
|
||||||
.expect("failed to write file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete every entry in `ftl_root` that is not mentioned in another message
|
/// Delete every entry in `ftl_root` that is not mentioned in another message
|
||||||
/// or any json in `json_root`.
|
/// or any json in `json_root`.
|
||||||
pub fn garbage_collect_ftl_entries(ftl_roots: &[impl AsRef<str>], json_root: impl AsRef<str>) {
|
pub fn garbage_collect_ftl_entries(args: GarbageCollectArgs) -> Result<()> {
|
||||||
let used_ftls = get_all_used_messages_and_terms(json_root.as_ref(), ftl_roots);
|
let used_ftls = get_all_used_messages_and_terms(&args.json_root, &args.ftl_roots);
|
||||||
strip_unused_ftl_messages_and_terms(ftl_roots, &used_ftls);
|
strip_unused_ftl_messages_and_terms(&args.ftl_roots, &used_ftls);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves every entry in `ftl_roots` that is not mentioned in another message, a
|
/// Moves every entry in `ftl_roots` that is not mentioned in another message, a
|
||||||
/// source file or any json in `json_roots` to the bottom of its file below a
|
/// source file or any json in `json_roots` to the bottom of its file below a
|
||||||
/// deprecation warning.
|
/// deprecation warning.
|
||||||
pub fn deprecate_ftl_entries(
|
pub fn deprecate_ftl_entries(args: DeprecateEntriesArgs) -> Result<()> {
|
||||||
ftl_roots: &[impl AsRef<str>],
|
let mut used_ftls = gather_ftl_references(&args.source_roots);
|
||||||
source_roots: &[impl AsRef<str>],
|
import_messages_from_json(&args.json_roots, &mut used_ftls);
|
||||||
json_roots: &[impl AsRef<str>],
|
extract_nested_messages_and_terms(&args.ftl_roots, &mut used_ftls);
|
||||||
) {
|
deprecate_unused_ftl_messages_and_terms(&args.ftl_roots, &used_ftls);
|
||||||
let mut used_ftls = gather_ftl_references(source_roots);
|
Ok(())
|
||||||
import_messages_from_json(json_roots, &mut used_ftls);
|
|
||||||
extract_nested_messages_and_terms(ftl_roots, &mut used_ftls);
|
|
||||||
deprecate_unused_ftl_messages_and_terms(ftl_roots, &used_ftls);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_used_messages_and_terms(
|
fn get_all_used_messages_and_terms(
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
pub mod garbage_collection;
|
||||||
|
pub mod serialize;
|
||||||
|
|
||||||
|
mod sync;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use clap::Subcommand;
|
||||||
|
use garbage_collection::deprecate_ftl_entries;
|
||||||
|
use garbage_collection::garbage_collect_ftl_entries;
|
||||||
|
use garbage_collection::write_ftl_json;
|
||||||
|
use garbage_collection::DeprecateEntriesArgs;
|
||||||
|
use garbage_collection::GarbageCollectArgs;
|
||||||
|
use garbage_collection::WriteJsonArgs;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Command {
|
||||||
|
/// Update commit references to the latest translations,
|
||||||
|
/// and copy source files to the translation repos. Requires access to the
|
||||||
|
/// i18n repos to run.
|
||||||
|
Sync,
|
||||||
|
/// Extract references from all Rust, Python, TS, Svelte and Designer files
|
||||||
|
/// in the given roots, convert them to ftl names case and write them as
|
||||||
|
/// a json to the target file.
|
||||||
|
WriteJson(WriteJsonArgs),
|
||||||
|
/// Delete every entry in the ftl files that is not mentioned in another
|
||||||
|
/// message or a given json.
|
||||||
|
GarbageCollect(GarbageCollectArgs),
|
||||||
|
/// Deprecate unused ftl entries by moving them to the bottom of the file
|
||||||
|
/// and adding a deprecation warning. An entry is considered unused if
|
||||||
|
/// cannot be found in a source or JSON file.
|
||||||
|
Deprecate(DeprecateEntriesArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
match Cli::parse().command {
|
||||||
|
Command::Sync => sync::sync(),
|
||||||
|
Command::WriteJson(args) => write_ftl_json(args),
|
||||||
|
Command::GarbageCollect(args) => garbage_collect_ftl_entries(args),
|
||||||
|
Command::Deprecate(args) => deprecate_ftl_entries(args),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,13 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
//! A helper script to update commit references to the latest translations,
|
|
||||||
//! and copy source files to the translation repos. Requires access to the
|
|
||||||
//! i18n repos to run.
|
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
use anki_process::CommandExt;
|
||||||
|
use anyhow::bail;
|
||||||
|
use anyhow::Context;
|
||||||
|
use anyhow::Result;
|
||||||
use camino::Utf8Path;
|
use camino::Utf8Path;
|
||||||
use snafu::prelude::*;
|
|
||||||
use snafu::Whatever;
|
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Whatever>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Module {
|
struct Module {
|
||||||
|
@ -23,8 +19,7 @@ struct Module {
|
||||||
/// remote is used to push via authenticated ssh.
|
/// remote is used to push via authenticated ssh.
|
||||||
const GIT_REMOTE: &str = "ssh";
|
const GIT_REMOTE: &str = "ssh";
|
||||||
|
|
||||||
#[snafu::report]
|
pub fn sync() -> Result<()> {
|
||||||
fn main() -> Result<()> {
|
|
||||||
let modules = [
|
let modules = [
|
||||||
Module {
|
Module {
|
||||||
template_folder: "ftl/core".into(),
|
template_folder: "ftl/core".into(),
|
||||||
|
@ -41,8 +36,7 @@ fn main() -> Result<()> {
|
||||||
fetch_new_translations(&module)?;
|
fetch_new_translations(&module)?;
|
||||||
push_new_templates(&module)?;
|
push_new_templates(&module)?;
|
||||||
}
|
}
|
||||||
commit(".", "Update translations")
|
commit(".", "Update translations").context("failure expected if no translations changed")?;
|
||||||
.whatever_context("failure expected if no translations changed")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,47 +44,41 @@ fn check_clean() -> Result<()> {
|
||||||
let output = Command::new("git")
|
let output = Command::new("git")
|
||||||
.arg("diff")
|
.arg("diff")
|
||||||
.output()
|
.output()
|
||||||
.whatever_context("git diff")?;
|
.context("git diff")?;
|
||||||
ensure_whatever!(output.status.success(), "git diff");
|
if !output.status.success() {
|
||||||
ensure_whatever!(
|
bail!("git diff");
|
||||||
output.stdout.is_empty(),
|
|
||||||
"please commit any outstanding changes first"
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
if !output.stdout.is_empty() {
|
||||||
fn run(command: &mut Command) -> Result<()> {
|
bail!("please commit any outstanding changes first");
|
||||||
let status = command
|
|
||||||
.status()
|
|
||||||
.with_whatever_context(|_| format!("{:?}", command))?;
|
|
||||||
if !status.success() {
|
|
||||||
whatever!("{:?} exited with code: {:?}", command, status.code());
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_new_translations(module: &Module) -> Result<()> {
|
fn fetch_new_translations(module: &Module) -> Result<()> {
|
||||||
run(Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(module.translation_repo)
|
.current_dir(module.translation_repo)
|
||||||
.args(["checkout", "main"]))?;
|
.args(["checkout", "main"])
|
||||||
run(Command::new("git")
|
.ensure_success()?;
|
||||||
|
Command::new("git")
|
||||||
.current_dir(module.translation_repo)
|
.current_dir(module.translation_repo)
|
||||||
.args(["pull", "origin", "main"]))?;
|
.args(["pull", "origin", "main"])
|
||||||
|
.ensure_success()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_new_templates(module: &Module) -> Result<()> {
|
fn push_new_templates(module: &Module) -> Result<()> {
|
||||||
run(Command::new("rsync")
|
Command::new("rsync")
|
||||||
.args(["-ai", "--delete", "--no-perms", "--no-times", "-c"])
|
.args(["-ai", "--delete", "--no-perms", "--no-times", "-c"])
|
||||||
.args([
|
.args([
|
||||||
format!("{}/", module.template_folder),
|
format!("{}/", module.template_folder),
|
||||||
format!("{}/", module.translation_repo.join("templates")),
|
format!("{}/", module.translation_repo.join("templates")),
|
||||||
]))?;
|
])
|
||||||
|
.ensure_success()?;
|
||||||
let changes_pending = !Command::new("git")
|
let changes_pending = !Command::new("git")
|
||||||
.current_dir(module.translation_repo)
|
.current_dir(module.translation_repo)
|
||||||
.args(["diff", "--exit-code"])
|
.args(["diff", "--exit-code"])
|
||||||
.status()
|
.status()
|
||||||
.whatever_context("git")?
|
.context("git")?
|
||||||
.success();
|
.success();
|
||||||
if changes_pending {
|
if changes_pending {
|
||||||
commit(module.translation_repo, "Update templates")?;
|
commit(module.translation_repo, "Update templates")?;
|
||||||
|
@ -100,11 +88,15 @@ fn push_new_templates(module: &Module) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(repo: &Utf8Path) -> Result<()> {
|
fn push(repo: &Utf8Path) -> Result<()> {
|
||||||
run(Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(repo)
|
.current_dir(repo)
|
||||||
.args(["push", GIT_REMOTE, "main"]))?;
|
.args(["push", GIT_REMOTE, "main"])
|
||||||
|
.ensure_success()?;
|
||||||
// ensure origin matches ssh remote
|
// ensure origin matches ssh remote
|
||||||
run(Command::new("git").current_dir(repo).args(["fetch"]))?;
|
Command::new("git")
|
||||||
|
.current_dir(repo)
|
||||||
|
.args(["fetch"])
|
||||||
|
.ensure_success()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +104,9 @@ fn commit<F>(folder: F, message: &str) -> Result<()>
|
||||||
where
|
where
|
||||||
F: AsRef<str>,
|
F: AsRef<str>,
|
||||||
{
|
{
|
||||||
run(Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(folder.as_ref())
|
.current_dir(folder.as_ref())
|
||||||
.args(["commit", "-a", "-m", message]))?;
|
.args(["commit", "-a", "-m", message])
|
||||||
|
.ensure_success()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "anki_i18n_helpers"
|
|
||||||
version.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
publish = false
|
|
||||||
rust-version.workspace = true
|
|
||||||
description = "Helpers for Anki's i18n system"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "anki_i18n_helpers"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
fluent-syntax.workspace = true
|
|
||||||
lazy_static.workspace = true
|
|
||||||
regex.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
walkdir.workspace = true
|
|
|
@ -1,49 +0,0 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
||||||
|
|
||||||
/// Deprecate unused ftl entries by moving them to the bottom of the file and
|
|
||||||
/// adding a deprecation warning. An entry is considered unused if cannot be
|
|
||||||
/// found in a source or JSON file.
|
|
||||||
/// Arguments before `--` are roots of ftl files, arguments after that are
|
|
||||||
/// source roots. JSON roots must be preceded by `--keep` or `-k`.
|
|
||||||
fn main() {
|
|
||||||
let args = Arguments::new();
|
|
||||||
anki_i18n_helpers::garbage_collection::deprecate_ftl_entries(
|
|
||||||
&args.ftl_roots,
|
|
||||||
&args.source_roots,
|
|
||||||
&args.json_roots,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct Arguments {
|
|
||||||
ftl_roots: Vec<String>,
|
|
||||||
source_roots: Vec<String>,
|
|
||||||
json_roots: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Arguments {
|
|
||||||
fn new() -> Self {
|
|
||||||
let mut args = Self::default();
|
|
||||||
let mut past_separator = false;
|
|
||||||
let mut keep_flag = false;
|
|
||||||
for arg in std::env::args() {
|
|
||||||
match arg.as_str() {
|
|
||||||
"--" => {
|
|
||||||
past_separator = true;
|
|
||||||
}
|
|
||||||
"--keep" | "-k" => {
|
|
||||||
keep_flag = true;
|
|
||||||
}
|
|
||||||
_ if keep_flag => {
|
|
||||||
keep_flag = false;
|
|
||||||
args.json_roots.push(arg)
|
|
||||||
}
|
|
||||||
_ if past_separator => args.source_roots.push(arg),
|
|
||||||
_ => args.ftl_roots.push(arg),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
args
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
||||||
|
|
||||||
/// Delete every entry in the ftl files that is not mentioned in another message
|
|
||||||
/// or a given json.
|
|
||||||
/// First argument is the root of the json files, following are the roots of the
|
|
||||||
/// ftl files.
|
|
||||||
fn main() {
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
|
||||||
anki_i18n_helpers::garbage_collection::garbage_collect_ftl_entries(&args[2..], &args[1]);
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
||||||
|
|
||||||
/// Extract references from all Rust, Python, TS, Svelte and Designer files in
|
|
||||||
/// the given roots, convert them to ftl names case and write them as a json to
|
|
||||||
/// the target file.
|
|
||||||
/// First argument is the target file name, following are source roots.
|
|
||||||
fn main() {
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
|
||||||
anki_i18n_helpers::garbage_collection::write_ftl_json(&args[2..], &args[1]);
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
||||||
|
|
||||||
pub mod garbage_collection;
|
|
||||||
pub mod serialize;
|
|
Loading…
Reference in New Issue