Skip to content

Commit

Permalink
(i18n) Provide translation strings for Registry
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeltlombardi committed Dec 6, 2024
1 parent 47f2245 commit a86dea4
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 42 deletions.
6 changes: 6 additions & 0 deletions registry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down
87 changes: 87 additions & 0 deletions registry/locales/cli.yml
Original file line number Diff line number Diff line change
@@ -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.
35 changes: 35 additions & 0 deletions registry/locales/errors.yml
Original file line number Diff line number Diff line change
@@ -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}
5 changes: 5 additions & 0 deletions registry/locales/resource.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
_version: 2
resource:
what_if:
create_key:
en: "key: %{subkey} not found, would create it"
55 changes: 28 additions & 27 deletions registry/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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<String>,
#[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<String>,
#[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,
}
17 changes: 9 additions & 8 deletions registry/src/error.rs
Original file line number Diff line number Diff line change
@@ -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,
}
22 changes: 18 additions & 4 deletions registry/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
};
Expand All @@ -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;
}
}
Expand Down
13 changes: 10 additions & 3 deletions registry/src/registry_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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),
Expand All @@ -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::<u16>::from_str(subkey_name) else {
return Err(RegistryError::Utf16Conversion("subkey_name".to_string()));
};
Expand Down

0 comments on commit a86dea4

Please sign in to comment.