149 lines
4.2 KiB
Rust
149 lines
4.2 KiB
Rust
use std::{
|
|
io::{BufReader, Read, Write},
|
|
path::PathBuf,
|
|
process::Command,
|
|
};
|
|
|
|
use clap::{Parser, Subcommand};
|
|
|
|
use icu_locid::{langid, LanguageIdentifier};
|
|
use l10n_db::{
|
|
self, js, read_file,
|
|
xliff::{self, import_file},
|
|
Bundle, Editor, ReadError,
|
|
};
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Parser)]
|
|
#[command(version, about, long_about = None)]
|
|
struct Cli {
|
|
#[command(subcommand)]
|
|
command: Option<Commands>,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Commands {
|
|
/// Edit, potentially creating, a key
|
|
EditKey {
|
|
#[arg(short, long)]
|
|
name: String,
|
|
#[arg(short, long)]
|
|
locale: String,
|
|
},
|
|
/// List al keys in the database
|
|
ListKeys,
|
|
// Search the database
|
|
// Search { },
|
|
Import {
|
|
#[arg(short, long)]
|
|
file: String,
|
|
},
|
|
/// Export the database
|
|
Export {
|
|
#[arg(short, long)]
|
|
format: String,
|
|
#[arg(short, long)]
|
|
locale: Option<String>,
|
|
},
|
|
Report,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct Config {
|
|
db_path: PathBuf,
|
|
base_locale: LanguageIdentifier,
|
|
locales: Vec<LanguageIdentifier>,
|
|
}
|
|
|
|
fn edit_key(bundle: &mut Bundle, key: String, locale: LanguageIdentifier, editor: &str) {
|
|
let message = bundle.message(key);
|
|
Editor::edit(message, locale, editor);
|
|
bundle.save();
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
struct Report {
|
|
keys: Vec<String>,
|
|
source_deleted: Vec<String>,
|
|
out_of_date: Vec<String>,
|
|
}
|
|
|
|
fn generate_report(
|
|
bundle: &Bundle,
|
|
base_locale: &LanguageIdentifier,
|
|
locales: Vec<LanguageIdentifier>,
|
|
) -> Report {
|
|
let mut report: Report = Default::default();
|
|
let locales: Vec<LanguageIdentifier> =
|
|
locales.into_iter().filter(|a| a != base_locale).collect();
|
|
for (key, message) in bundle.message_iter() {
|
|
match message.variant(base_locale) {
|
|
Some(ref base_variant) => {
|
|
for locale in locales.iter() {
|
|
match message.variant(locale) {
|
|
Some(v) if v.modified() < base_variant.modified() => {
|
|
report.out_of_date.push(key.to_owned())
|
|
}
|
|
Some(_) => {}
|
|
None => report.out_of_date.push(key.to_owned()),
|
|
}
|
|
}
|
|
}
|
|
None => {
|
|
report.source_deleted.push(key.clone());
|
|
}
|
|
}
|
|
let base_variant = message.variant(base_locale).clone();
|
|
}
|
|
|
|
report
|
|
}
|
|
|
|
fn main() {
|
|
let editor = std::env::var("EDITOR").expect("Set EDITOR to the path to your favorite editor");
|
|
|
|
let config: Config = read_file(&PathBuf::from("./config.toml"))
|
|
.and_then(|bytes| String::from_utf8(bytes).map_err(|_| ReadError::InvalidFormat))
|
|
.and_then(|content| toml::from_str(&content).map_err(|_| ReadError::InvalidFormat))
|
|
.unwrap();
|
|
|
|
let cli = Cli::parse();
|
|
|
|
let mut bundle = Bundle::load_from_disk(PathBuf::from(&config.db_path));
|
|
|
|
match &cli.command {
|
|
Some(Commands::EditKey { name, locale }) => {
|
|
let identifier = locale.parse::<LanguageIdentifier>().unwrap();
|
|
edit_key(&mut bundle, name.to_owned(), identifier, &editor)
|
|
}
|
|
Some(Commands::ListKeys) => {
|
|
for (key, _) in bundle.message_iter() {
|
|
println!("{}", key);
|
|
}
|
|
}
|
|
Some(Commands::Import { file }) => {
|
|
import_file(&mut bundle, &PathBuf::from(file)).unwrap();
|
|
bundle.save();
|
|
}
|
|
Some(Commands::Export { format, locale }) => {
|
|
let locale = locale
|
|
.as_ref()
|
|
.map(|l| l.clone().parse::<LanguageIdentifier>().unwrap())
|
|
.unwrap_or(langid!("en"));
|
|
|
|
match format.as_ref() {
|
|
"js" => js::export_file(&bundle, locale, &PathBuf::from("output.json")).unwrap(),
|
|
"xliff" => {
|
|
xliff::export_file(&bundle, locale, &PathBuf::from("output.xliff")).unwrap()
|
|
}
|
|
_ => todo!(),
|
|
}
|
|
}
|
|
Some(Commands::Report) => {
|
|
let report = generate_report(&bundle, &config.base_locale, config.locales);
|
|
println!("{:?}", report);
|
|
}
|
|
None => {}
|
|
}
|
|
}
|