From a86dea4a9a52f51389d416a5c6462f27e4a3f57e Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Fri, 6 Dec 2024 11:20:53 -0600 Subject: [PATCH] (i18n) Provide translation strings for Registry --- registry/Cargo.toml | 6 +++ registry/locales/cli.yml | 87 +++++++++++++++++++++++++++++++++ registry/locales/errors.yml | 35 +++++++++++++ registry/locales/resource.yml | 5 ++ registry/src/args.rs | 55 +++++++++++---------- registry/src/error.rs | 17 ++++--- registry/src/main.rs | 22 +++++++-- registry/src/registry_helper.rs | 13 +++-- 8 files changed, 198 insertions(+), 42 deletions(-) create mode 100644 registry/locales/cli.yml create mode 100644 registry/locales/errors.yml create mode 100644 registry/locales/resource.yml diff --git a/registry/Cargo.toml b/registry/Cargo.toml index b8c8a8f6..cff402e0 100644 --- a/registry/Cargo.toml +++ b/registry/Cargo.toml @@ -3,6 +3,11 @@ name = "registry" version = "1.0.0" edition = "2021" +[package.metadata.i18n] +available-locales = ["en"] +default-locale = "en" +load-path = "locales" + [profile.release] strip = true # optimize for size @@ -14,6 +19,7 @@ lto = true clap = { version = "4.5", features = ["derive"] } crossterm = "0.28" registry = "1.2" +rust-i18n = "3" schemars = "0.8" serde = "1.0" serde_json = "1.0" diff --git a/registry/locales/cli.yml b/registry/locales/cli.yml new file mode 100644 index 00000000..d8ee3680 --- /dev/null +++ b/registry/locales/cli.yml @@ -0,0 +1,87 @@ +_version: 2 +cli: + about: + en: Manage state of Windows registry + # config command help + config: + about: + en: Manage registry configuration. + args: + input: + help: + en: The registry JSON input. + what_if: + help: + en: Run as a what-if operation instead of applying the registry configuration. + get: + about: + en: Retrieve registry configuration. + set: + about: + en: Apply registry configuration. + delete: + about: + en: Delete registry configuration. + # query command help + query: + about: + en: Query a registry key or value. + args: + key_path: + help: + en: The registry key path to query. + value_name: + help: + en: The name of the value to query. + recurse: + help: + en: Recursively query subkeys. + # set command help + set: + about: + en: Set a registry key or value. + args: + key_path: + help: + en: The registry key path to set. + value: + help: + en: The value to set. + # remove command help + remove: + about: + en: Remove a registry key or value. + args: + key_path: + help: + en: The registry key path to remove. + value_name: + help: + en: The name of the value to remove. + recurse: + help: + en: Recursively remove subkeys. + # find command help + find: + about: + en: Find a registry key or value. + args: + key_path: + help: + en: The registry key path to start find. + find: + help: + en: The string to find. + recurse: + help: + en: Recursively find. + keys_only: + help: + en: Only find keys. + values_only: + help: + en: Only find values. + # schema command help + schema: + about: + en: Retrieve JSON schema. diff --git a/registry/locales/errors.yml b/registry/locales/errors.yml new file mode 100644 index 00000000..f33b41ce --- /dev/null +++ b/registry/locales/errors.yml @@ -0,0 +1,35 @@ +_version: 2 +errors: + InvalidHive: + en: Invalid hive + Json: + en: JSON + Registry: + en: Registry + RegistryKey: + en: Registry key + RegistryKeyNotFound: + en: Registry key not found + RegistryValue: + en: Registry value + Utf16Conversion: + en: UTF-16 conversion failed due to interior NULL values for + UnsupportedValueDataType: + en: Unsupported registry value data type + # internal, not an enum + eprint: + tracing_init: + en: Unable to set global default tracing subscriber. Tracing is diabled. + debug: + attach: + en: attach debugger to pid %{pid} and press any key to continue + event: + read: + en: "Error: Failed to read event: %{err}" + unexpexted: + en: "Unexpected event: %{e:?}" + remove: + key_not_exist: + en: Key already does not exist + deleting_subkey: + en: Deleting subkey '%{name}' using %{parent} diff --git a/registry/locales/resource.yml b/registry/locales/resource.yml new file mode 100644 index 00000000..088aa8ad --- /dev/null +++ b/registry/locales/resource.yml @@ -0,0 +1,5 @@ +_version: 2 +resource: + what_if: + create_key: + en: "key: %{subkey} not found, would create it" diff --git a/registry/src/args.rs b/registry/src/args.rs index a0521ec9..8b0760e9 100644 --- a/registry/src/args.rs +++ b/registry/src/args.rs @@ -2,9 +2,10 @@ // Licensed under the MIT License. use clap::{Parser, Subcommand}; +use rust_i18n::t; #[derive(Parser)] -#[clap(name = "registry", version = "0.0.1", about = "Manage state of Windows registry", long_about = None)] +#[clap(name = "registry", version = "0.0.1", about = t!("cli.about").to_string(), long_about = None)] pub struct Arguments { #[clap(subcommand)] @@ -13,70 +14,70 @@ pub struct Arguments { #[derive(Debug, PartialEq, Eq, Subcommand)] pub enum ConfigSubCommand { - #[clap(name = "get", about = "Retrieve registry configuration.")] + #[clap(name = "get", about = t!("cli.config.get.about").to_string())] Get { - #[clap(short, long, required = true, help = "The registry JSON input.")] + #[clap(short, long, required = true, help = t!("cli.config.args.input.help").to_string())] input: String, }, - #[clap(name = "set", about = "Apply registry configuration.")] + #[clap(name = "set", about = t!("cli.config.set.about").to_string())] Set { - #[clap(short, long, required = true, help = "The registry JSON input.")] + #[clap(short, long, required = true, help = t!("cli.config.args.input.help").to_string())] input: String, - #[clap(short = 'w', long, help = "Run as a what-if operation instead of applying the registry configuration")] + #[clap(short = 'w', long, help = t!("cli.config.args.what_if.help").to_string())] what_if: bool, }, - #[clap(name = "delete", about = "Delete registry configuration.")] + #[clap(name = "delete", about = t!("cli.config.delete.about").to_string())] Delete { - #[clap(short, long, required = true, help = "The registry JSON input.")] + #[clap(short, long, required = true, help = t!("cli.config.args.input.help").to_string())] input: String, }, } #[derive(Debug, PartialEq, Eq, Subcommand)] pub enum SubCommand { - #[clap(name = "query", about = "Query a registry key or value.", arg_required_else_help = true)] + #[clap(name = "query", about = t!("cli.query.about").to_string(), arg_required_else_help = true)] Query { - #[clap(short, long, required = true, help = "The registry key path to query.")] + #[clap(short, long, required = true, help = t!("cli.query.args.key_path.help").to_string())] key_path: String, - #[clap(short, long, help = "The name of the value to query.")] + #[clap(short, long, help = t!("cli.query.args.value_name.help").to_string())] value_name: Option, - #[clap(short, long, help = "Recursively query subkeys.")] + #[clap(short, long, help = t!("cli.query.args.recurse.help").to_string())] recurse: bool, }, - #[clap(name = "set", about = "Set a registry key or value.")] + #[clap(name = "set", about = t!("cli.set.about").to_string())] Set { - #[clap(short, long, required = true, help = "The registry key path to set.")] + #[clap(short, long, required = true, help = t!("cli.set.args.key_path.help").to_string())] key_path: String, - #[clap(short, long, help = "The value to set.")] + #[clap(short, long, help = t!("cli.set.args.value.help").to_string())] value: String, }, - #[clap(name = "remove", about = "Remove a registry key or value.", arg_required_else_help = true)] + #[clap(name = "remove", about = t!("cli.remove.about").to_string(), arg_required_else_help = true)] Remove { - #[clap(short, long, required = true, help = "The registry key path to remove.")] + #[clap(short, long, required = true, help = t!("cli.remove.args.key_path.help").to_string())] key_path: String, - #[clap(short, long, help = "The name of the value to remove.")] + #[clap(short, long, help = t!("cli.remove.args.value_name.help").to_string())] value_name: Option, - #[clap(short, long, help = "Recursively remove subkeys.")] + #[clap(short, long, help = t!("cli.remove.args.recurse.help").to_string())] recurse: bool, }, - #[clap(name = "find", about = "Find a registry key or value.", arg_required_else_help = true)] + #[clap(name = "find", about = t!("cli.find.about").to_string(), arg_required_else_help = true)] Find { - #[clap(short, long, required = true, help = "The registry key path to start find.")] + #[clap(short, long, required = true, help = t!("cli.find.args.key_path.help").to_string())] key_path: String, - #[clap(short, long, required = true, help = "The string to find.")] + #[clap(short, long, required = true, help = t!("cli.find.args.find.help").to_string())] find: String, - #[clap(short, long, help = "Recursively find.")] + #[clap(short, long, help = t!("cli.find.args.recurse.help").to_string())] recurse: bool, - #[clap(long, help = "Only find keys.")] + #[clap(long, help = t!("cli.find.args.keys_only.help").to_string())] keys_only: bool, - #[clap(long, help = "Only find values.")] + #[clap(long, help = t!("cli.find.args.values_only.help").to_string())] values_only: bool, }, - #[clap(name = "config", about = "Manage registry configuration.", arg_required_else_help = true)] + #[clap(name = "config", about = t!("cli.config.about").to_string(), arg_required_else_help = true)] Config { #[clap(subcommand)] subcommand: ConfigSubCommand, }, - #[clap(name = "schema", about = "Retrieve JSON schema.")] + #[clap(name = "schema", about = t!("cli.schema.about").to_string())] Schema, } diff --git a/registry/src/error.rs b/registry/src/error.rs index 0c768fbf..be013a18 100644 --- a/registry/src/error.rs +++ b/registry/src/error.rs @@ -1,32 +1,33 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use rust_i18n::t; use thiserror::Error; #[derive(Error, Debug)] #[allow(clippy::module_name_repetitions)] pub enum RegistryError { - #[error("Invalid hive: {0}.")] + #[error("{t}: {0}", t = t!("errors.InvalidHive"))] InvalidHive(String), - #[error("JSON: {0}")] + #[error("{t}: {0}", t = t!("errors.Json"))] Json(#[from] serde_json::Error), - #[error("Registry: {0}")] + #[error("{t}: {0}", t = t!("errors.Registry"))] Registry(#[from] registry::Error), - #[error("Registry key: {0}")] + #[error("{t}: {0}", t = t!("errors.RegistryKey"))] RegistryKey(#[from] registry::key::Error), - #[error("Registry key not found: {0}")] + #[error("{t}: {0}", t = t!("errors.RegistryKeyNotFound"))] RegistryKeyNotFound(String), - #[error("Registry value: {0}")] + #[error("{t}: {0}", t = t!("errors.RegistryValue"))] RegistryValue(#[from] registry::value::Error), - #[error("UTF-16 conversion of {0} failed due to interior NULL values")] + #[error("{t}: {0}", t = t!("errors.Utf16Conversion"))] Utf16Conversion(String), - #[error("Unsupported registry value data type")] + #[error("{t}", t = t!("errors.UnsupportedValueDataType"))] UnsupportedValueDataType, } diff --git a/registry/src/main.rs b/registry/src/main.rs index 7b078d79..3dd2f0c9 100644 --- a/registry/src/main.rs +++ b/registry/src/main.rs @@ -6,6 +6,13 @@ use crossterm::event; #[cfg(debug_assertions)] use std::env; +// #[macro_use] +// extern crate rust_i18n; + +// Init translations using the `[package.metadata.i18n]` section in `Cargo.toml` +use rust_i18n::t; +rust_i18n::i18n!("locales"); + use args::Arguments; use clap::Parser; use registry_helper::RegistryHelper; @@ -135,19 +142,24 @@ pub fn enable_tracing() { let subscriber = tracing_subscriber::Registry::default().with(fmt).with(filter); if tracing::subscriber::set_global_default(subscriber).is_err() { - eprintln!("Unable to set global default tracing subscriber. Tracing is diabled."); + let msg = t!("error.eprint.tracing_init"); + eprintln!("{msg}"); } } #[cfg(debug_assertions)] fn check_debug() { if env::var("DEBUG_REGISTRY").is_ok() { - eprintln!("attach debugger to pid {} and press any key to continue", std::process::id()); + let msg = t!("error.eprint.debug.attach", pid = std::process::id()); + eprintln!("{msg}"); + // eprintln!("attach debugger to pid {} and press any key to continue", std::process::id()); loop { let event = match event::read() { Ok(event) => event, Err(err) => { - eprintln!("Error: Failed to read event: {err}"); + let msg = t!("error.eprint.debug.event.read", "err" => err); + eprintln!("{msg}"); + // eprintln!("Error: Failed to read event: {err}"); break; } }; @@ -157,7 +169,9 @@ fn check_debug() { break; } } else { - eprintln!("Unexpected event: {event:?}"); + let msg = t!("error.eprint.debug.event.unexpected", e = event : {:?}); + eprintln!("{msg}"); + // eprintln!("Unexpected event: {event:?}"); continue; } } diff --git a/registry/src/registry_helper.rs b/registry/src/registry_helper.rs index e7975a1b..123c73cd 100644 --- a/registry/src/registry_helper.rs +++ b/registry/src/registry_helper.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. use registry::{Data, Hive, RegKey, Security, key, value}; +use rust_i18n::t; use utfx::{U16CString, UCString}; use crate::config::{Metadata, Registry, RegistryValueData}; use crate::error::RegistryError; @@ -99,7 +100,8 @@ impl RegistryHelper { }; if self.what_if { - what_if_metadata.push(format!("key: {subkey} not found, would create it")); + what_if_metadata.push(t!("resource.what_if.create_key", "subkey" => subkey).to_string()); + // what_if_metadata.push(format!("key: {subkey} not found, would create it")); } else { reg_key = reg_key.create(path, Security::CreateSubKey)?; @@ -185,7 +187,9 @@ impl RegistryHelper { Ok(reg_key) => reg_key, // handle NotFound error Err(RegistryError::RegistryKeyNotFound(_)) => { - eprintln!("Key already does not exist"); + let msg = t!("error.eprint.remove.key_not_exist"); + eprintln!("{msg}"); + // eprintln!("Key already does not exist"); return Ok(()); }, Err(e) => return Err(e), @@ -200,7 +204,10 @@ impl RegistryHelper { // get the subkey name let subkey_name = &self.config.key_path[parent_path.len() + 1..]; - eprintln!("Deleting subkey '{subkey_name}' using {parent_reg_key}"); + + let msg = t!("error.eprint.remove.deleting_subkey", name = subkey_name, parent = parent_reg_key); + eprintln!("{msg}"); + // eprintln!("Deleting subkey '{subkey_name}' using {parent_reg_key}"); let Ok(subkey_name) = UCString::::from_str(subkey_name) else { return Err(RegistryError::Utf16Conversion("subkey_name".to_string())); };