From da61ac87473116f22ff9f3fdfbf003b951f2debc Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 29 Dec 2024 20:26:47 +0000 Subject: [PATCH 01/16] wip: see what it would take to incorporate rust-i18n --- crates/atuin-client/Cargo.toml | 1 + crates/atuin-client/locales/app.yml | 17 ++++ crates/atuin-client/src/lib.rs | 9 +++ crates/atuin-client/src/settings.rs | 23 ++++-- crates/atuin/Cargo.toml | 2 + crates/atuin/locales/app.yml | 81 +++++++++++++++++++ .../src/command/client/search/duration.rs | 5 +- .../src/command/client/search/history_list.rs | 4 +- .../src/command/client/search/interactive.rs | 43 +++++----- crates/atuin/src/main.rs | 10 +++ 10 files changed, 164 insertions(+), 31 deletions(-) create mode 100644 crates/atuin-client/locales/app.yml create mode 100644 crates/atuin/locales/app.yml diff --git a/crates/atuin-client/Cargo.toml b/crates/atuin-client/Cargo.toml index 56d1e6d250f..3e9731ddc1a 100644 --- a/crates/atuin-client/Cargo.toml +++ b/crates/atuin-client/Cargo.toml @@ -73,6 +73,7 @@ palette = { version = "0.7.5", features = ["serializing"] } lazy_static = "1.4.0" strum_macros = "0.26.3" strum = { version = "0.26.2", features = ["strum_macros"] } +rust-i18n = "3.1.2" [dev-dependencies] tokio = { version = "1", features = ["full"] } diff --git a/crates/atuin-client/locales/app.yml b/crates/atuin-client/locales/app.yml new file mode 100644 index 00000000000..707b1753d2d --- /dev/null +++ b/crates/atuin-client/locales/app.yml @@ -0,0 +1,17 @@ +DIRECTORY: + en: DIRECTORY + ga: COMHADLANN +GLOBAL: + en: GLOBAL + ga: UILÍOCH +HOST: + en: HOST + ga: ÓSTACH +SESSION: + en: SESSION + ga: SEISIÚN +WORKSPACE: + en: WORKSPACE + ga: SPÁS OIBRE +_version: 2 + diff --git a/crates/atuin-client/src/lib.rs b/crates/atuin-client/src/lib.rs index d0f6ee7313d..66082ef973b 100644 --- a/crates/atuin-client/src/lib.rs +++ b/crates/atuin-client/src/lib.rs @@ -1,5 +1,8 @@ #![forbid(unsafe_code)] +#[macro_use] +extern crate rust_i18n; + #[macro_use] extern crate log; @@ -23,3 +26,9 @@ pub mod settings; pub mod theme; mod utils; + +i18n!("locales", fallback = "en"); + +pub fn set_locale(locale: &str) { + rust_i18n::set_locale(locale) +} diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs index 4c2b10ab165..19c088ad150 100644 --- a/crates/atuin-client/src/settings.rs +++ b/crates/atuin-client/src/settings.rs @@ -2,6 +2,7 @@ use std::{ collections::HashMap, convert::TryFrom, fmt, io::prelude::*, path::PathBuf, str::FromStr, }; +use lazy_static::lazy_static; use atuin_common::record::HostId; use clap::ValueEnum; use config::{ @@ -67,7 +68,7 @@ impl SearchMode { } } -#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum, Serialize)] +#[derive(Clone, Debug, Deserialize, Copy, PartialEq, Eq, ValueEnum, Serialize, Hash)] pub enum FilterMode { #[serde(rename = "global")] Global = 0, @@ -85,15 +86,21 @@ pub enum FilterMode { Workspace = 4, } +lazy_static! { + static ref FILTER_MODES: HashMap = { + HashMap::from([ + (FilterMode::Global, t!("GLOBAL").into_owned()), + (FilterMode::Host, t!("HOST").into_owned()), + (FilterMode::Session, t!("SESSION").into_owned()), + (FilterMode::Directory, t!("DIRECTORY").into_owned()), + (FilterMode::Workspace, t!("WORKSPACE").into_owned()), + ]) + }; +} + impl FilterMode { pub fn as_str(&self) -> &'static str { - match self { - FilterMode::Global => "GLOBAL", - FilterMode::Host => "HOST", - FilterMode::Session => "SESSION", - FilterMode::Directory => "DIRECTORY", - FilterMode::Workspace => "WORKSPACE", - } + FILTER_MODES.get(self).unwrap().as_str() } } diff --git a/crates/atuin/Cargo.toml b/crates/atuin/Cargo.toml index 0dcf135a233..af5faa37157 100644 --- a/crates/atuin/Cargo.toml +++ b/crates/atuin/Cargo.toml @@ -80,6 +80,8 @@ tracing-subscriber = { workspace = true } uuid = { workspace = true } sysinfo = "0.30.7" regex = "1.10.5" +rust-i18n = "3" +sys-locale = "0.3.2" [target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies] arboard = { version = "3.4", optional = true } diff --git a/crates/atuin/locales/app.yml b/crates/atuin/locales/app.yml new file mode 100644 index 00000000000..62d50156afc --- /dev/null +++ b/crates/atuin/locales/app.yml @@ -0,0 +1,81 @@ +_version: 2 +: + en: + ga: +: + en: + ga: <éalaigh> +: + en: + ga: +Drawing inspector, but no stats: + en: Drawing inspector, but no stats + ga: Ag tarraingt cigire, ach níl statisticí ann +Nothing to inspect: + en: Nothing to inspect + ga: Níl aon rud le scrúdú +SRCH: + en: SRCH + ga: LORG +"The inspector is new - please give feedback (good, or bad) at https://forum.atuin.sh": + en: "The inspector is new - please give feedback (good, or bad) at https://forum.atuin.sh" + ga: "Tá an cigire nua - tabhair aiseolas le do thoil (go maith nó go holc) ag https://forum.atuin.sh" +delete: + en: delete + ga: scrios +edit: + en: edit + ga: cuir in eager +exit: + en: exit + ga: scoir +inspect: + en: inspect + ga: scrúdaigh +invalid tab index on input: + en: cluaisín neamhbhailí ar ionchur + ga: invalid tab index on input +keys.ctrl-and: + en: + ga: +search: + en: search + ga: cuardach +should have been handled!: + en: should have been handled! + ga: ba chóir a bheith láimhseáilte! +Search: + en: Search + ga: Cuardach +Inspect: + en: Inspect + ga: Scrúdaigh +history count: + en: history count + ga: comhaireamh na staire +"%{time} ago": + en: "%{time} ago" + ga: "%{time} ó shin" +time.y: + en: "%{value}y" + ga: "%{value}b" +time.mo: + en: "%{value}mo" + ga: "%{value}mí" +time.d: + en: "%{value}d" + ga: "%{value}l" +time.h: + en: "%{value}h" + ga: "%{value}u" +time.m: + en: "%{value}m" + ga: "%{value}n" +time.s: + en: "%{value}s" +time.ms: + en: "%{value}ms" +time.us: + en: "%{value}us" +time.ns: + en: "%{value}ns" diff --git a/crates/atuin/src/command/client/search/duration.rs b/crates/atuin/src/command/client/search/duration.rs index dfa9426b54b..d62218e2da1 100644 --- a/crates/atuin/src/command/client/search/duration.rs +++ b/crates/atuin/src/command/client/search/duration.rs @@ -48,7 +48,10 @@ pub fn format_duration_into(dur: Duration, f: &mut fmt::Formatter<'_>) -> fmt::R } match fmt(dur) { - ControlFlow::Break((unit, value)) => write!(f, "{value}{unit}"), + ControlFlow::Break((unit, value)) => { + let unit_str = t!(format!("time.{}", unit), value=value); + write!(f, "{}", unit_str) + }, ControlFlow::Continue(()) => write!(f, "0s"), } } diff --git a/crates/atuin/src/command/client/search/history_list.rs b/crates/atuin/src/command/client/search/history_list.rs index 792e89b86ed..01999e86508 100644 --- a/crates/atuin/src/command/client/search/history_list.rs +++ b/crates/atuin/src/command/client/search/history_list.rs @@ -197,8 +197,8 @@ impl DrawState<'_> { usize::from(PREFIX_LENGTH).saturating_sub(usize::from(self.x) + 4 + time.len()); self.draw(&SPACES[..padding], Style::default()); - self.draw(&time, style.into()); - self.draw(" ago", style.into()); + let ago = t!("%{time} ago", time=time); + self.draw(format!(" {}", ago).to_string().as_str(), style.into()); } fn command(&mut self, h: &History) { diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs index c1a678f0872..ccab6ed9415 100644 --- a/crates/atuin/src/command/client/search/interactive.rs +++ b/crates/atuin/src/command/client/search/interactive.rs @@ -2,6 +2,7 @@ use std::{ io::{stdout, Write}, time::Duration, }; +use rust_i18n::t; use atuin_common::utils::{self, Escapable as _}; use eyre::Result; @@ -247,7 +248,7 @@ impl State { 1 => super::inspector::input(self, settings, self.results_state.selected(), input), - _ => panic!("invalid tab index on input"), + _ => panic!("{}", t!("invalid tab index on input")), }; self.prefix = false; @@ -654,7 +655,7 @@ impl State { // TODO: this should be split so that we have one interactive search container that is // EITHER a search box or an inspector. But I'm not doing that now, way too much atm. // also allocate less 🙈 - let titles: Vec<_> = TAB_TITLES.iter().copied().map(Line::from).collect(); + let titles: Vec<_> = TAB_TITLES.iter().copied().map(|s| t!(s)).map(Line::from).collect(); if show_tabs { let tabs = Tabs::new(titles) @@ -719,7 +720,7 @@ impl State { 1 => { if results.is_empty() { - let message = Paragraph::new("Nothing to inspect") + let message = Paragraph::new(t!("Nothing to inspect")) .block( Block::new() .title(Title::from(" Info ".to_string())) @@ -734,14 +735,14 @@ impl State { f, results_list_chunk, &results[self.results_state.selected()], - &stats.expect("Drawing inspector, but no stats"), + &stats.expect(t!("Drawing inspector, but no stats").to_string().as_str()), theme, ); } // HACK: I'm following up with abstracting this into the UI container, with a // sub-widget for search + for inspector - let feedback = Paragraph::new("The inspector is new - please give feedback (good, or bad) at https://forum.atuin.sh"); + let feedback = Paragraph::new(t!("The inspector is new - please give feedback (good, or bad) at https://forum.atuin.sh")); f.render_widget(feedback, input_chunk); return; @@ -803,32 +804,32 @@ impl State { match self.tab_index { // search 0 => Paragraph::new(Text::from(Line::from(vec![ - Span::styled("", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(": exit"), + Span::styled(t!(""), Style::default().add_modifier(Modifier::BOLD)), + Span::raw(format!(": {}", t!("exit"))), Span::raw(", "), - Span::styled("", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(": edit"), + Span::styled(t!(""), Style::default().add_modifier(Modifier::BOLD)), + Span::raw(format!(": {}", t!("edit"))), Span::raw(", "), - Span::styled("", Style::default().add_modifier(Modifier::BOLD)), + Span::styled(t!(""), Style::default().add_modifier(Modifier::BOLD)), Span::raw(if settings.enter_accept { ": run" } else { ": edit" }), Span::raw(", "), - Span::styled("", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(": inspect"), + Span::styled(t!("keys.ctrl-and", key="o"), Style::default().add_modifier(Modifier::BOLD)), + Span::raw(format!(": {}", t!("inspect"))), ]))), 1 => Paragraph::new(Text::from(Line::from(vec![ Span::styled("", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(": exit"), + Span::raw(format!(": {}", t!("exit"))), Span::raw(", "), - Span::styled("", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(": search"), + Span::styled(t!("keys.ctrl-and", key="o"), Style::default().add_modifier(Modifier::BOLD)), + Span::raw(format!(": {}", t!("search"))), Span::raw(", "), - Span::styled("", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(": delete"), + Span::styled(t!("keys.ctrl-and", key="d"), Style::default().add_modifier(Modifier::BOLD)), + Span::raw(format!(": {}", t!("delete"))), ]))), _ => unreachable!("invalid tab index"), @@ -839,7 +840,8 @@ impl State { fn build_stats(&self, theme: &Theme) -> Paragraph { let stats = Paragraph::new(Text::from(Span::raw(format!( - "history count: {}", + "{}: {}", + t!("history count"), self.history_count, )))) .style(theme.as_style(Meaning::Annotation)) @@ -885,8 +887,9 @@ impl State { fn build_input(&self, style: StyleState) -> Paragraph { /// Max width of the UI box showing current mode const MAX_WIDTH: usize = 14; + let srch_string: String = format!(" {}:", t!("SRCH").to_string()); let (pref, mode) = if self.switched_search_mode { - (" SRCH:", self.search_mode.as_str()) + (srch_string.as_str(), self.search_mode.as_str()) } else { ("", self.search.filter_mode.as_str()) }; @@ -1201,7 +1204,7 @@ pub async fn history( Ok(app.search.input.into_inner()) } InputAction::Continue | InputAction::Redraw | InputAction::Delete(_) => { - unreachable!("should have been handled!") + unreachable!("{}", t!("should have been handled!").to_string()) } } } diff --git a/crates/atuin/src/main.rs b/crates/atuin/src/main.rs index eaa58664c40..2b9d0112251 100644 --- a/crates/atuin/src/main.rs +++ b/crates/atuin/src/main.rs @@ -1,8 +1,11 @@ #![warn(clippy::pedantic, clippy::nursery)] #![allow(clippy::use_self, clippy::missing_const_for_fn)] // not 100% reliable +#[macro_use] +extern crate rust_i18n; use clap::Parser; use eyre::Result; +use sys_locale::get_locale; use command::AtuinCmd; @@ -24,6 +27,8 @@ static HELP_TEMPLATE: &str = "\ {all-args}{after-help}"; +i18n!("locales", fallback = "en"); + /// Magical shell history #[derive(Parser)] #[command( @@ -38,6 +43,11 @@ struct Atuin { impl Atuin { fn run(self) -> Result<()> { + let locale = get_locale().unwrap_or_else(|| String::from("en")); + + rust_i18n::set_locale(locale.as_str()); + atuin_client::set_locale(locale.as_str()); + self.atuin.run() } } From 03a3b8217a85110864d0a9e3f85f2b26058448df Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 29 Dec 2024 20:52:52 +0000 Subject: [PATCH 02/16] chore: tidy up unnecessary verbosity and formatting --- crates/atuin-client/src/settings.rs | 2 +- .../src/command/client/search/duration.rs | 6 ++-- .../src/command/client/search/history_list.rs | 4 +-- .../src/command/client/search/interactive.rs | 30 ++++++++++++++----- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs index 19c088ad150..a09ee45e637 100644 --- a/crates/atuin-client/src/settings.rs +++ b/crates/atuin-client/src/settings.rs @@ -2,7 +2,6 @@ use std::{ collections::HashMap, convert::TryFrom, fmt, io::prelude::*, path::PathBuf, str::FromStr, }; -use lazy_static::lazy_static; use atuin_common::record::HostId; use clap::ValueEnum; use config::{ @@ -11,6 +10,7 @@ use config::{ use eyre::{bail, eyre, Context, Error, Result}; use fs_err::{create_dir_all, File}; use humantime::parse_duration; +use lazy_static::lazy_static; use regex::RegexSet; use semver::Version; use serde::{Deserialize, Serialize}; diff --git a/crates/atuin/src/command/client/search/duration.rs b/crates/atuin/src/command/client/search/duration.rs index d62218e2da1..bdb790633e2 100644 --- a/crates/atuin/src/command/client/search/duration.rs +++ b/crates/atuin/src/command/client/search/duration.rs @@ -49,9 +49,9 @@ pub fn format_duration_into(dur: Duration, f: &mut fmt::Formatter<'_>) -> fmt::R match fmt(dur) { ControlFlow::Break((unit, value)) => { - let unit_str = t!(format!("time.{}", unit), value=value); - write!(f, "{}", unit_str) - }, + let unit_str = t!(format!("time.{}", unit), value = value); + write!(f, "{unit_str}") + } ControlFlow::Continue(()) => write!(f, "0s"), } } diff --git a/crates/atuin/src/command/client/search/history_list.rs b/crates/atuin/src/command/client/search/history_list.rs index 01999e86508..f63c07a9eb2 100644 --- a/crates/atuin/src/command/client/search/history_list.rs +++ b/crates/atuin/src/command/client/search/history_list.rs @@ -197,8 +197,8 @@ impl DrawState<'_> { usize::from(PREFIX_LENGTH).saturating_sub(usize::from(self.x) + 4 + time.len()); self.draw(&SPACES[..padding], Style::default()); - let ago = t!("%{time} ago", time=time); - self.draw(format!(" {}", ago).to_string().as_str(), style.into()); + let ago = t!("%{time} ago", time = time); + self.draw(format!(" {ago}").as_str(), style.into()); } fn command(&mut self, h: &History) { diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs index ccab6ed9415..4672c1ab51d 100644 --- a/crates/atuin/src/command/client/search/interactive.rs +++ b/crates/atuin/src/command/client/search/interactive.rs @@ -1,8 +1,8 @@ +use rust_i18n::t; use std::{ io::{stdout, Write}, time::Duration, }; -use rust_i18n::t; use atuin_common::utils::{self, Escapable as _}; use eyre::Result; @@ -655,7 +655,12 @@ impl State { // TODO: this should be split so that we have one interactive search container that is // EITHER a search box or an inspector. But I'm not doing that now, way too much atm. // also allocate less 🙈 - let titles: Vec<_> = TAB_TITLES.iter().copied().map(|s| t!(s)).map(Line::from).collect(); + let titles: Vec<_> = TAB_TITLES + .iter() + .copied() + .map(|s| t!(s)) + .map(Line::from) + .collect(); if show_tabs { let tabs = Tabs::new(titles) @@ -735,7 +740,7 @@ impl State { f, results_list_chunk, &results[self.results_state.selected()], - &stats.expect(t!("Drawing inspector, but no stats").to_string().as_str()), + &stats.expect("Drawing inspector, but no stats"), theme, ); } @@ -817,7 +822,10 @@ impl State { ": edit" }), Span::raw(", "), - Span::styled(t!("keys.ctrl-and", key="o"), Style::default().add_modifier(Modifier::BOLD)), + Span::styled( + t!("keys.ctrl-and", key = "o"), + Style::default().add_modifier(Modifier::BOLD), + ), Span::raw(format!(": {}", t!("inspect"))), ]))), @@ -825,10 +833,16 @@ impl State { Span::styled("", Style::default().add_modifier(Modifier::BOLD)), Span::raw(format!(": {}", t!("exit"))), Span::raw(", "), - Span::styled(t!("keys.ctrl-and", key="o"), Style::default().add_modifier(Modifier::BOLD)), + Span::styled( + t!("keys.ctrl-and", key = "o"), + Style::default().add_modifier(Modifier::BOLD), + ), Span::raw(format!(": {}", t!("search"))), Span::raw(", "), - Span::styled(t!("keys.ctrl-and", key="d"), Style::default().add_modifier(Modifier::BOLD)), + Span::styled( + t!("keys.ctrl-and", key = "d"), + Style::default().add_modifier(Modifier::BOLD), + ), Span::raw(format!(": {}", t!("delete"))), ]))), @@ -887,7 +901,7 @@ impl State { fn build_input(&self, style: StyleState) -> Paragraph { /// Max width of the UI box showing current mode const MAX_WIDTH: usize = 14; - let srch_string: String = format!(" {}:", t!("SRCH").to_string()); + let srch_string: String = format!(" {}:", t!("SRCH")); let (pref, mode) = if self.switched_search_mode { (srch_string.as_str(), self.search_mode.as_str()) } else { @@ -1204,7 +1218,7 @@ pub async fn history( Ok(app.search.input.into_inner()) } InputAction::Continue | InputAction::Redraw | InputAction::Delete(_) => { - unreachable!("{}", t!("should have been handled!").to_string()) + unreachable!("{}", t!("should have been handled!")) } } } From 1d3a89176ef190ea53400a9e0e5a759e8ed2de40 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 29 Dec 2024 21:03:44 +0000 Subject: [PATCH 03/16] fix: add for atuin_client --- crates/atuin/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/atuin/src/main.rs b/crates/atuin/src/main.rs index 2b9d0112251..5518f078b4a 100644 --- a/crates/atuin/src/main.rs +++ b/crates/atuin/src/main.rs @@ -7,6 +7,8 @@ use clap::Parser; use eyre::Result; use sys_locale::get_locale; +use atuin_client; + use command::AtuinCmd; mod command; From f62b91ff6d85b211a58a90dc6f31183c3f6cbe61 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 14:44:55 +0000 Subject: [PATCH 04/16] chore: tidy-up line for clippy --- crates/atuin/src/main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/atuin/src/main.rs b/crates/atuin/src/main.rs index 5518f078b4a..2b9d0112251 100644 --- a/crates/atuin/src/main.rs +++ b/crates/atuin/src/main.rs @@ -7,8 +7,6 @@ use clap::Parser; use eyre::Result; use sys_locale::get_locale; -use atuin_client; - use command::AtuinCmd; mod command; From 9e434ed2e358d69accfaac6db62b5c37c4afa7c2 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 15:41:47 +0000 Subject: [PATCH 05/16] fix(i18n): restructure to let i18n be set centrally (as possible) and minimize logic per-crate --- crates/atuin-client/src/lib.rs | 4 ---- crates/atuin-common/Cargo.toml | 2 ++ crates/atuin-common/src/lib.rs | 6 ++++++ crates/atuin-common/src/utils.rs | 10 ++++++++++ crates/atuin/Cargo.toml | 1 - crates/atuin/src/command/client.rs | 2 ++ crates/atuin/src/command/mod.rs | 3 +++ crates/atuin/src/main.rs | 6 ------ 8 files changed, 23 insertions(+), 11 deletions(-) diff --git a/crates/atuin-client/src/lib.rs b/crates/atuin-client/src/lib.rs index 66082ef973b..024fa1d6246 100644 --- a/crates/atuin-client/src/lib.rs +++ b/crates/atuin-client/src/lib.rs @@ -28,7 +28,3 @@ pub mod theme; mod utils; i18n!("locales", fallback = "en"); - -pub fn set_locale(locale: &str) { - rust_i18n::set_locale(locale) -} diff --git a/crates/atuin-common/Cargo.toml b/crates/atuin-common/Cargo.toml index af255eadc2e..6f696f312a9 100644 --- a/crates/atuin-common/Cargo.toml +++ b/crates/atuin-common/Cargo.toml @@ -25,6 +25,8 @@ directories = { workspace = true } sysinfo = "0.30.7" base64 = { workspace = true } getrandom = "0.2" +sys-locale = "0.3.2" +rust-i18n = "3.1.2" lazy_static = "1.4.0" diff --git a/crates/atuin-common/src/lib.rs b/crates/atuin-common/src/lib.rs index 9a94aa62e86..334d5647302 100644 --- a/crates/atuin-common/src/lib.rs +++ b/crates/atuin-common/src/lib.rs @@ -1,5 +1,11 @@ #![forbid(unsafe_code)] +#[macro_use] +extern crate rust_i18n; + +pub static DEFAULT_LOCALE: &'static str = "en"; +i18n!("locales", fallback = "en"); + /// Defines a new UUID type wrapper macro_rules! new_uuid { ($name:ident) => { diff --git a/crates/atuin-common/src/utils.rs b/crates/atuin-common/src/utils.rs index 7f156d77ef1..91e42afdc08 100644 --- a/crates/atuin-common/src/utils.rs +++ b/crates/atuin-common/src/utils.rs @@ -2,6 +2,8 @@ use std::borrow::Cow; use std::env; use std::path::PathBuf; +use sys_locale::get_locale as sys_get_locale; + use eyre::{eyre, Result}; use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; @@ -43,6 +45,14 @@ pub fn has_git_dir(path: &str) -> bool { gitdir.exists() } +pub fn set_locale() { + rust_i18n::set_locale(get_locale().as_str()) +} + +pub fn get_locale() -> String { + sys_get_locale().unwrap_or_else(|| String::from(crate::DEFAULT_LOCALE)) +} + // detect if any parent dir has a git repo in it // I really don't want to bring in libgit for something simple like this // If we start to do anything more advanced, then perhaps diff --git a/crates/atuin/Cargo.toml b/crates/atuin/Cargo.toml index af5faa37157..6d223bc251d 100644 --- a/crates/atuin/Cargo.toml +++ b/crates/atuin/Cargo.toml @@ -81,7 +81,6 @@ uuid = { workspace = true } sysinfo = "0.30.7" regex = "1.10.5" rust-i18n = "3" -sys-locale = "0.3.2" [target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies] arboard = { version = "3.4", optional = true } diff --git a/crates/atuin/src/command/client.rs b/crates/atuin/src/command/client.rs index 2637b691b30..a4c0da46cc3 100644 --- a/crates/atuin/src/command/client.rs +++ b/crates/atuin/src/command/client.rs @@ -94,6 +94,8 @@ pub enum Cmd { impl Cmd { pub fn run(self) -> Result<()> { + atuin_common::utils::set_locale(); + let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() .build() diff --git a/crates/atuin/src/command/mod.rs b/crates/atuin/src/command/mod.rs index 09df430ed9e..771cc2a4902 100644 --- a/crates/atuin/src/command/mod.rs +++ b/crates/atuin/src/command/mod.rs @@ -1,5 +1,6 @@ use clap::Subcommand; use eyre::Result; +use rust_i18n::set_locale; #[cfg(not(windows))] use rustix::{fs::Mode, process::umask}; @@ -37,6 +38,8 @@ pub enum AtuinCmd { impl AtuinCmd { pub fn run(self) -> Result<()> { + set_locale(atuin_common::utils::get_locale().as_str()); + #[cfg(not(windows))] { // set umask before we potentially open/create files diff --git a/crates/atuin/src/main.rs b/crates/atuin/src/main.rs index 2b9d0112251..98f3c2634d2 100644 --- a/crates/atuin/src/main.rs +++ b/crates/atuin/src/main.rs @@ -5,7 +5,6 @@ extern crate rust_i18n; use clap::Parser; use eyre::Result; -use sys_locale::get_locale; use command::AtuinCmd; @@ -43,11 +42,6 @@ struct Atuin { impl Atuin { fn run(self) -> Result<()> { - let locale = get_locale().unwrap_or_else(|| String::from("en")); - - rust_i18n::set_locale(locale.as_str()); - atuin_client::set_locale(locale.as_str()); - self.atuin.run() } } From 4e90cb3778966aa13ec6f134e0c781dd860ae0fa Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 20:42:15 +0000 Subject: [PATCH 06/16] feat(i18n): make client and common translatable --- crates/atuin-client/src/api_client.rs | 48 +++++++++++++-------------- crates/atuin-client/src/database.rs | 38 ++++++++++----------- crates/atuin-client/src/encryption.rs | 28 ++++++++-------- crates/atuin-client/src/history.rs | 2 +- crates/atuin-client/src/kv.rs | 16 ++++----- crates/atuin-client/src/login.rs | 10 +++--- crates/atuin-client/src/logout.rs | 6 ++-- crates/atuin-common/src/api.rs | 2 +- crates/atuin-common/src/lib.rs | 2 +- crates/atuin-common/src/shell.rs | 16 ++++----- crates/atuin-common/src/utils.rs | 10 +++--- crates/atuin/src/command/client.rs | 2 +- 12 files changed, 90 insertions(+), 90 deletions(-) diff --git a/crates/atuin-client/src/api_client.rs b/crates/atuin-client/src/api_client.rs index 19820b29a0b..e99c0ea634c 100644 --- a/crates/atuin-client/src/api_client.rs +++ b/crates/atuin-client/src/api_client.rs @@ -50,7 +50,7 @@ pub async fn register( let resp = reqwest::get(url).await?; if resp.status().is_success() { - bail!("username already in use"); + bail!(t!("username already in use")); } let url = format!("{address}/register"); @@ -65,7 +65,7 @@ pub async fn register( let resp = handle_resp_error(resp).await?; if !ensure_version(&resp)? { - bail!("could not register user due to version mismatch"); + bail!(t!("could not register user due to version mismatch")); } let session = resp.json::().await?; @@ -85,7 +85,7 @@ pub async fn login(address: &str, req: LoginRequest) -> Result { let resp = handle_resp_error(resp).await?; if !ensure_version(&resp)? { - bail!("Could not login due to version mismatch"); + bail!(t!("Could not login due to version mismatch")); } let session = resp.json::().await?; @@ -118,17 +118,17 @@ pub fn ensure_version(response: &Response) -> Result { let version = if let Some(version) = version { match version.to_str() { Ok(v) => Version::parse(v), - Err(e) => bail!("failed to parse server version: {:?}", e), + Err(e) => bail!("{}: {:?}", t!("failed to parse server version"), e), } } else { - bail!("Server not reporting its version: it is either too old or unhealthy"); + bail!(t!("Server not reporting its version: it is either too old or unhealthy")); }?; // If the client is newer than the server if version.major < ATUIN_VERSION.major { - println!("Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin"); - println!("Client: {}", ATUIN_CARGO_VERSION); - println!("Server: {}", version); + println!("{}", t!("Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin")); + println!("{}: {}", t!("Client"), ATUIN_CARGO_VERSION); + println!("{}: {}", t!("Server"), version); return Ok(false); } @@ -141,12 +141,12 @@ async fn handle_resp_error(resp: Response) -> Result { if status == StatusCode::SERVICE_UNAVAILABLE { bail!( - "Service unavailable: check https://status.atuin.sh (or get in touch with your host)" + t!("Service unavailable: check https://status.atuin.sh (or get in touch with your host)") ); } if status == StatusCode::TOO_MANY_REQUESTS { - bail!("Rate limited; please wait before doing that again"); + bail!(t!("Rate limited; please wait before doing that again")); } if !status.is_success() { @@ -154,13 +154,13 @@ async fn handle_resp_error(resp: Response) -> Result { let reason = error.reason; if status.is_client_error() { - bail!("Invalid request to the service: {status} - {reason}.") + bail!("{}: {status} - {reason}.", t!("Invalid request to the service")) } - bail!("There was an error with the atuin sync service, server error {status}: {reason}.\nIf the problem persists, contact the host") + bail!(t!("There was an error with the atuin sync service, server error %{status}: %{reason}.\nIf the problem persists, contact the host", status=status, reason=reason)) } - bail!("There was an error with the atuin sync service: Status {status:?}.\nIf the problem persists, contact the host") + bail!(t!("There was an error with the atuin sync service: Status %{status:?}.\nIf the problem persists, contact the host", status=status)) } Ok(resp) @@ -198,11 +198,11 @@ impl<'a> Client<'a> { let resp = handle_resp_error(resp).await?; if !ensure_version(&resp)? { - bail!("could not sync due to version mismatch"); + bail!(t!("could not sync due to version mismatch")); } if resp.status() != StatusCode::OK { - bail!("failed to get count (are you logged in?)"); + bail!(t!("failed to get count (are you logged in?)")); } let count = resp.json::().await?; @@ -218,7 +218,7 @@ impl<'a> Client<'a> { let resp = handle_resp_error(resp).await?; if !ensure_version(&resp)? { - bail!("could not sync due to version mismatch"); + bail!(t!("could not sync due to version mismatch")); } let status = resp.json::().await?; @@ -304,7 +304,7 @@ impl<'a> Client<'a> { let url = format!("{}/api/v0/record", self.sync_addr); let url = Url::parse(url.as_str())?; - debug!("uploading {} records to {url}", records.len()); + debug!("{}", t!("uploading %{records} records to %{url}", records=records.len(), url=url)); let resp = self.client.post(url).json(records).send().await?; handle_resp_error(resp).await?; @@ -349,12 +349,12 @@ impl<'a> Client<'a> { let resp = handle_resp_error(resp).await?; if !ensure_version(&resp)? { - bail!("could not sync records due to version mismatch"); + bail!(t!("could not sync records due to version mismatch")); } let index = resp.json().await?; - debug!("got remote index {:?}", index); + debug!("{} {:?}", t!("got remote index"), index); Ok(index) } @@ -366,11 +366,11 @@ impl<'a> Client<'a> { let resp = self.client.delete(url).send().await?; if resp.status() == 403 { - bail!("invalid login details"); + bail!(t!("invalid login details")); } else if resp.status() == 200 { Ok(()) } else { - bail!("Unknown error"); + bail!(t!("Unknown error")); } } @@ -393,13 +393,13 @@ impl<'a> Client<'a> { .await?; if resp.status() == 401 { - bail!("current password is incorrect") + bail!(t!("current password is incorrect")) } else if resp.status() == 403 { - bail!("invalid login details"); + bail!(t!("invalid login details")); } else if resp.status() == 200 { Ok(()) } else { - bail!("Unknown error"); + bail!(t!("Unknown error")); } } diff --git a/crates/atuin-client/src/database.rs b/crates/atuin-client/src/database.rs index 5bdbb75ce9c..95f039454a7 100644 --- a/crates/atuin-client/src/database.rs +++ b/crates/atuin-client/src/database.rs @@ -55,12 +55,12 @@ pub struct OptFilters { pub fn current_context() -> Context { let Ok(session) = env::var("ATUIN_SESSION") else { - eprintln!("ERROR: Failed to find $ATUIN_SESSION in the environment. Check that you have correctly set up your shell."); + eprintln!("{}", t!("ERROR: Failed to find $ATUIN_SESSION in the environment. Check that you have correctly set up your shell.")); std::process::exit(1); }; let hostname = get_host_user(); let cwd = utils::get_current_dir(); - let host_id = Settings::host_id().expect("failed to load host ID"); + let host_id = Settings::host_id().expect(&t!("failed to load host ID")); let git_root = utils::in_git_repo(cwd.as_str()); Context { @@ -226,7 +226,7 @@ impl Sqlite { #[async_trait] impl Database for Sqlite { async fn save(&self, h: &History) -> Result<()> { - debug!("saving history to sqlite"); + debug!("{}", t!("saving history to sqlite")); let mut tx = self.pool.begin().await?; Self::save_raw(&mut tx, h).await?; tx.commit().await?; @@ -235,7 +235,7 @@ impl Database for Sqlite { } async fn save_bulk(&self, h: &[History]) -> Result<()> { - debug!("saving history to sqlite"); + debug!("{}", t!("saving history to sqlite")); let mut tx = self.pool.begin().await?; @@ -249,7 +249,7 @@ impl Database for Sqlite { } async fn load(&self, id: &str) -> Result> { - debug!("loading history item {}", id); + debug!("{} {}", t!("loading history item"), id); let res = sqlx::query("select * from history where id = ?1") .bind(id) @@ -261,7 +261,7 @@ impl Database for Sqlite { } async fn update(&self, h: &History) -> Result<()> { - debug!("updating sqlite history"); + debug!("{}", t!("updating sqlite history")); sqlx::query( "update history @@ -292,7 +292,7 @@ impl Database for Sqlite { unique: bool, include_deleted: bool, ) -> Result> { - debug!("listing history"); + debug!(t!("listing history")); let mut query = SqlBuilder::select_from(SqlName::new("history").alias("h").baquoted()); query.field("*").order_desc("timestamp"); @@ -324,7 +324,7 @@ impl Database for Sqlite { query.limit(max); } - let query = query.sql().expect("bug in list query. please report"); + let query = query.sql().expect(&t!("bug in list query. please report")); let res = sqlx::query(&query) .map(Self::query_history) @@ -335,7 +335,7 @@ impl Database for Sqlite { } async fn range(&self, from: OffsetDateTime, to: OffsetDateTime) -> Result> { - debug!("listing history from {:?} to {:?}", from, to); + debug!("{}", t!("listing history from %{from:?} to %{to:?}", from=from, to=to)); let res = sqlx::query( "select * from history where timestamp >= ?1 and timestamp <= ?2 order by timestamp asc", @@ -559,7 +559,7 @@ impl Database for Sqlite { sql.and_where_is_null("deleted_at"); - let query = sql.sql().expect("bug in search query. please report"); + let query = sql.sql().expect(&t!("bug in search query. please report")); let res = sqlx::query(&query) .map(Self::query_history) @@ -579,7 +579,7 @@ impl Database for Sqlite { } async fn all_with_count(&self) -> Result> { - debug!("listing history"); + debug!("{}", t!("listing history")); let mut query = SqlBuilder::select_from(SqlName::new("history").alias("h").baquoted()); @@ -601,7 +601,7 @@ impl Database for Sqlite { .and_where("deleted_at is null") .order_desc("timestamp"); - let query = query.sql().expect("bug in list query. please report"); + let query = query.sql().expect(&t!("bug in list query. please report")); let res = sqlx::query(&query) .map(|row: SqliteRow| { @@ -694,15 +694,15 @@ impl Database for Sqlite { .group_by("month_year") .having("duration > 0"); - let prev = prev.sql().expect("issue in stats previous query"); - let next = next.sql().expect("issue in stats next query"); - let total = total.sql().expect("issue in stats average query"); - let average = average.sql().expect("issue in stats previous query"); - let exits = exits.sql().expect("issue in stats exits query"); - let day_of_week = day_of_week.sql().expect("issue in stats day of week query"); + let prev = prev.sql().expect(&t!("issue in stats previous query")); + let next = next.sql().expect(&t!("issue in stats next query")); + let total = total.sql().expect(&t!("issue in stats average query")); + let average = average.sql().expect(&t!("issue in stats previous query")); + let exits = exits.sql().expect(&t!("issue in stats exits query")); + let day_of_week = day_of_week.sql().expect(&t!("issue in stats day of week query")); let duration_over_time = duration_over_time .sql() - .expect("issue in stats duration over time query"); + .expect(&t!("issue in stats duration over time query")); let prev = sqlx::query(&prev) .bind(h.timestamp.unix_timestamp_nanos() as i64) diff --git a/crates/atuin-client/src/encryption.rs b/crates/atuin-client/src/encryption.rs index 02e5e3fb69d..d1c544633f6 100644 --- a/crates/atuin-client/src/encryption.rs +++ b/crates/atuin-client/src/encryption.rs @@ -42,7 +42,7 @@ pub fn new_key(settings: &Settings) -> Result { let path = PathBuf::from(path); if path.exists() { - bail!("key already exists! cannot overwrite"); + bail!(t!("key already exists! cannot overwrite")); } let (key, encoded) = generate_encoded_key()?; @@ -70,10 +70,10 @@ pub fn load_key(settings: &Settings) -> Result { pub fn encode_key(key: &Key) -> Result { let mut buf = vec![]; rmp::encode::write_array_len(&mut buf, key.len() as u32) - .wrap_err("could not encode key to message pack")?; + .wrap_err(t!("could not encode key to message pack"))?; for b in key { rmp::encode::write_uint(&mut buf, *b as u64) - .wrap_err("could not encode key to message pack")?; + .wrap_err(t!("could not encode key to message pack"))?; } let buf = BASE64_STANDARD.encode(buf); @@ -85,7 +85,7 @@ pub fn decode_key(key: String) -> Result { let buf = BASE64_STANDARD .decode(key.trim_end()) - .wrap_err("encryption key is not a valid base64 encoding")?; + .wrap_err(t!("encryption key is not a valid base64 encoding"))?; // old code wrote the key as a fixed length array of 32 bytes // new code writes the key with a length prefix @@ -97,14 +97,14 @@ pub fn decode_key(key: String) -> Result { match Marker::from_u8(buf[0]) { Marker::Bin8 => { let len = decode::read_bin_len(&mut bytes).map_err(|err| eyre!("{err:?}"))?; - ensure!(len == 32, "encryption key is not the correct size"); + ensure!(len == 32, t!("encryption key is not the correct size")); let key = <[u8; 32]>::try_from(bytes.remaining_slice()) - .context("could not decode encryption key")?; + .context(t!("could not decode encryption key"))?; Ok(key.into()) } Marker::Array16 => { let len = decode::read_array_len(&mut bytes).map_err(|err| eyre!("{err:?}"))?; - ensure!(len == 32, "encryption key is not the correct size"); + ensure!(len == 32, t!("encryption key is not the correct size")); let mut key = Key::default(); for i in &mut key { @@ -112,7 +112,7 @@ pub fn decode_key(key: String) -> Result { } Ok(key) } - _ => bail!("could not decode encryption key"), + _ => bail!(t!("could not decode encryption key")), } } } @@ -125,7 +125,7 @@ pub fn encrypt(history: &History, key: &Key) -> Result { let nonce = XSalsa20Poly1305::generate_nonce(&mut OsRng); XSalsa20Poly1305::new(key) .encrypt_in_place(&nonce, &[], &mut buf) - .map_err(|_| eyre!("could not encrypt"))?; + .map_err(|_| eyre!(t!("could not encrypt")))?; Ok(EncryptedHistory { ciphertext: buf, @@ -140,7 +140,7 @@ pub fn decrypt(mut encrypted_history: EncryptedHistory, key: &Key) -> Result Result { let nfields = decode::read_array_len(&mut bytes).map_err(error_report)?; if nfields < 8 { - bail!("malformed decrypted history") + bail!(t!("malformed decrypted history")) } if nfields > 9 { - bail!("cannot decrypt history from a newer version of atuin"); + bail!(t!("cannot decrypt history from a newer version of atuin")); } let bytes = bytes.remaining_slice(); @@ -241,7 +241,7 @@ fn decode(bytes: &[u8]) -> Result { } if !bytes.is_empty() { - bail!("trailing bytes in encoded history. malformed") + bail!(t!("trailing bytes in encoded history. malformed")) } Ok(History { @@ -305,7 +305,7 @@ mod test { }; // this should err - let _ = decrypt(e2, &key1).expect_err("expected an error decrypting with invalid key"); + let _ = decrypt(e2, &key1).expect_err(t!("expected an error decrypting with invalid key")); } #[test] diff --git a/crates/atuin-client/src/history.rs b/crates/atuin-client/src/history.rs index 0503b43deaa..19a26a7e075 100644 --- a/crates/atuin-client/src/history.rs +++ b/crates/atuin-client/src/history.rs @@ -375,7 +375,7 @@ impl History { pub fn should_save(&self, settings: &Settings) -> bool { let secret_regex = SECRET_PATTERNS.iter().map(|f| f.1); - let secret_regex = RegexSet::new(secret_regex).expect("Failed to build secrets regex"); + let secret_regex = RegexSet::new(secret_regex).expect(&t!("Failed to build secrets regex")); !(self.command.starts_with(' ') || settings.history_filter.is_match(&self.command) diff --git a/crates/atuin-client/src/kv.rs b/crates/atuin-client/src/kv.rs index 6c522a0e9e3..fb5bc6ff554 100644 --- a/crates/atuin-client/src/kv.rs +++ b/crates/atuin-client/src/kv.rs @@ -46,7 +46,7 @@ impl KvRecord { let mut bytes = decode::Bytes::new(&data.0); let nfields = decode::read_array_len(&mut bytes).map_err(error_report)?; - ensure!(nfields == 3, "too many entries in v0 kv record"); + ensure!(nfields == 3, t!("too many entries in v0 kv record")); let bytes = bytes.remaining_slice(); @@ -56,7 +56,7 @@ impl KvRecord { let (value, bytes) = decode::read_str_from_slice(bytes).map_err(error_report)?; if !bytes.is_empty() { - bail!("trailing bytes in encoded kvrecord. malformed") + bail!(t!("trailing bytes in encoded kvrecord. malformed")) } Ok(KvRecord { @@ -66,7 +66,7 @@ impl KvRecord { }) } _ => { - bail!("unknown version {version:?}") + bail!(t!("unknown version %{version:?}")) } } } @@ -97,10 +97,10 @@ impl KvStore { value: &str, ) -> Result<()> { if value.len() > KV_VAL_MAX_LEN { - return Err(eyre!( - "kv value too large: max len {} bytes", - KV_VAL_MAX_LEN - )); + return Err(eyre!(t!( + "kv value too large: max len {len} bytes", + len=KV_VAL_MAX_LEN + ))); } let record = KvRecord { @@ -176,7 +176,7 @@ impl KvStore { for record in tagged { let decrypted = match record.version.as_str() { KV_VERSION => record.decrypt::(encryption_key)?, - version => bail!("unknown version {version:?}"), + version => bail!("{} {version:?}", t!("unknown version"), version=version), }; let kv = KvRecord::deserialize(&decrypted.data, KV_VERSION)?; diff --git a/crates/atuin-client/src/login.rs b/crates/atuin-client/src/login.rs index 0530346401f..3fb7a9a289d 100644 --- a/crates/atuin-client/src/login.rs +++ b/crates/atuin-client/src/login.rs @@ -28,12 +28,12 @@ pub async fn login( // assume they copied in the base64 key bip39::ErrorKind::InvalidWord => key, bip39::ErrorKind::InvalidChecksum => { - bail!("key mnemonic was not valid") + bail!(t!("key mnemonic was not valid")) } bip39::ErrorKind::InvalidKeysize(_) | bip39::ErrorKind::InvalidWordLength(_) | bip39::ErrorKind::InvalidEntropyLength(_, _) => { - bail!("key was not the correct length") + bail!(t!("key was not the correct length")) } } } else { @@ -63,15 +63,15 @@ pub async fn login( let encoded = key.clone(); // gonna want to save it in a bit let new_key: [u8; 32] = decode_key(key) - .context("could not decode provided key - is not valid base64")? + .context(t!("could not decode provided key - is not valid base64"))? .into(); if new_key != current_key { - println!("\nRe-encrypting local store with new key"); + println!("\n{}", t!("Re-encrypting local store with new key")); store.re_encrypt(¤t_key, &new_key).await?; - println!("Writing new key"); + println!("{}", t!("Writing new key")); let mut file = File::create(key_path).await?; file.write_all(encoded.as_bytes()).await?; } diff --git a/crates/atuin-client/src/logout.rs b/crates/atuin-client/src/logout.rs index fe1a4d23034..d4754331194 100644 --- a/crates/atuin-client/src/logout.rs +++ b/crates/atuin-client/src/logout.rs @@ -7,10 +7,10 @@ pub fn logout(settings: &Settings) -> Result<()> { let session_path = settings.session_path.as_str(); if settings.logged_in() { - remove_file(session_path).context("Failed to remove session file")?; - println!("You have logged out!"); + remove_file(session_path).context(t!("Failed to remove session file"))?; + println!("{}", t!("You have logged out!")); } else { - println!("You are not logged in"); + println!("{}", t!("You are not logged in")); } Ok(()) diff --git a/crates/atuin-common/src/api.rs b/crates/atuin-common/src/api.rs index 4e897811888..cb59e810762 100644 --- a/crates/atuin-common/src/api.rs +++ b/crates/atuin-common/src/api.rs @@ -10,7 +10,7 @@ pub static ATUIN_CARGO_VERSION: &str = env!("CARGO_PKG_VERSION"); lazy_static! { pub static ref ATUIN_VERSION: Version = - Version::parse(ATUIN_CARGO_VERSION).expect("failed to parse self semver"); + Version::parse(ATUIN_CARGO_VERSION).expect(&t!("failed to parse self semver")); } #[derive(Debug, Serialize, Deserialize)] diff --git a/crates/atuin-common/src/lib.rs b/crates/atuin-common/src/lib.rs index 334d5647302..fade3891019 100644 --- a/crates/atuin-common/src/lib.rs +++ b/crates/atuin-common/src/lib.rs @@ -3,7 +3,7 @@ #[macro_use] extern crate rust_i18n; -pub static DEFAULT_LOCALE: &'static str = "en"; +pub static DEFAULT_LOCALE: &str = "en"; i18n!("locales", fallback = "en"); /// Defines a new UUID type wrapper diff --git a/crates/atuin-common/src/shell.rs b/crates/atuin-common/src/shell.rs index 32da6a8d09d..7a841db36a2 100644 --- a/crates/atuin-common/src/shell.rs +++ b/crates/atuin-common/src/shell.rs @@ -49,12 +49,12 @@ impl Shell { let sys = System::new_all(); let process = sys - .process(get_current_pid().expect("Failed to get current PID")) - .expect("Process with current pid does not exist"); + .process(get_current_pid().expect(&t!("Failed to get current PID"))) + .expect(&t!("Process with current pid does not exist")); let parent = sys - .process(process.parent().expect("Atuin running with no parent!")) - .expect("Process with parent pid does not exist"); + .process(process.parent().expect(&t!("Atuin running with no parent!"))) + .expect(&t!("Process with parent pid does not exist")); let shell = parent.name().trim().to_lowercase(); let shell = shell.strip_prefix('-').unwrap_or(&shell); @@ -163,11 +163,11 @@ pub fn shell_name(parent: Option<&Process>) -> String { parent } else { let process = sys - .process(get_current_pid().expect("Failed to get current PID")) - .expect("Process with current pid does not exist"); + .process(get_current_pid().expect(&t!("Failed to get current PID"))) + .expect(&t!("Process with current pid does not exist")); - sys.process(process.parent().expect("Atuin running with no parent!")) - .expect("Process with parent pid does not exist") + sys.process(process.parent().expect(&t!("Atuin running with no parent!"))) + .expect(&t!("Process with parent pid does not exist")) }; let shell = parent.name().trim().to_lowercase(); diff --git a/crates/atuin-common/src/utils.rs b/crates/atuin-common/src/utils.rs index 91e42afdc08..899982aa667 100644 --- a/crates/atuin-common/src/utils.rs +++ b/crates/atuin-common/src/utils.rs @@ -16,7 +16,7 @@ pub fn crypto_random_bytes() -> [u8; N] { // idea to use getrandom for things such as passwords. let mut ret = [0u8; N]; - getrandom(&mut ret).expect("Failed to generate random bytes!"); + getrandom(&mut ret).expect(&t!("Failed to generate random bytes!")); ret } @@ -77,13 +77,13 @@ pub fn in_git_repo(path: &str) -> Option { #[cfg(not(target_os = "windows"))] pub fn home_dir() -> PathBuf { - let home = std::env::var("HOME").expect("$HOME not found"); + let home = std::env::var("HOME").expect(&t!("$HOME not found")); PathBuf::from(home) } #[cfg(target_os = "windows")] pub fn home_dir() -> PathBuf { - let home = std::env::var("USERPROFILE").expect("%userprofile% not found"); + let home = std::env::var("USERPROFILE").expect(&t!("%userprofile% not found")); PathBuf::from(home) } @@ -175,7 +175,7 @@ pub trait Escapable: AsRef { pub fn unquote(s: &str) -> Result { if s.chars().count() < 2 { - return Err(eyre!("not enough chars")); + return Err(eyre!(t!("not enough chars"))); } let quote = s.chars().next().unwrap(); @@ -186,7 +186,7 @@ pub fn unquote(s: &str) -> Result { } if s.chars().last().unwrap() != quote { - return Err(eyre!("unexpected eof, quotes do not match")); + return Err(eyre!(t!("unexpected eof, quotes do not match"))); } // removes quote characters diff --git a/crates/atuin/src/command/client.rs b/crates/atuin/src/command/client.rs index a4c0da46cc3..5f682da3274 100644 --- a/crates/atuin/src/command/client.rs +++ b/crates/atuin/src/command/client.rs @@ -101,7 +101,7 @@ impl Cmd { .build() .unwrap(); - let settings = Settings::new().wrap_err("could not load client settings")?; + let settings = Settings::new().wrap_err(t!("could not load client settings"))?; let theme_manager = theme::ThemeManager::new(settings.theme.debug, None); let res = runtime.block_on(self.run_inner(settings, theme_manager)); From 344a3779d92accc1b295342bea6f79869c5f6855 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 20:50:41 +0000 Subject: [PATCH 07/16] feat(i18n): add default TODO.yml for translatable strings in common, client and atuin --- crates/atuin-client/locales/TODO.yml | 123 +++++++++++++++++++++++++++ crates/atuin-common/locales/TODO.yml | 22 +++++ crates/atuin/locales/TODO.yml | 4 + 3 files changed, 149 insertions(+) create mode 100644 crates/atuin-client/locales/TODO.yml create mode 100644 crates/atuin-common/locales/TODO.yml create mode 100644 crates/atuin/locales/TODO.yml diff --git a/crates/atuin-client/locales/TODO.yml b/crates/atuin-client/locales/TODO.yml new file mode 100644 index 00000000000..2e435034956 --- /dev/null +++ b/crates/atuin-client/locales/TODO.yml @@ -0,0 +1,123 @@ +Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin: + en: Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin +Client: + en: Client +Could not login due to version mismatch: + en: Could not login due to version mismatch +'ERROR: Failed to find $ATUIN_SESSION in the environment. Check that you have correctly set up your shell.': + en: 'ERROR: Failed to find $ATUIN_SESSION in the environment. Check that you have correctly set up your shell.' +Failed to build secrets regex: + en: Failed to build secrets regex +Failed to remove session file: + en: Failed to remove session file +Invalid request to the service: + en: Invalid request to the service +Rate limited; please wait before doing that again: + en: Rate limited; please wait before doing that again +Re-encrypting local store with new key: + en: Re-encrypting local store with new key +Server: + en: Server +'Server not reporting its version: it is either too old or unhealthy': + en: 'Server not reporting its version: it is either too old or unhealthy' +'Service unavailable: check https://status.atuin.sh (or get in touch with your host)': + en: 'Service unavailable: check https://status.atuin.sh (or get in touch with your host)' +'There was an error with the atuin sync service, server error %{status}: %{reason}. If the problem persists, contact the host': + en: 'There was an error with the atuin sync service, server error %{status}: %{reason}. If the problem persists, contact the host' +'There was an error with the atuin sync service: Status %{status:?}. If the problem persists, contact the host': + en: 'There was an error with the atuin sync service: Status %{status:?}. If the problem persists, contact the host' +Unknown error: + en: Unknown error +Writing new key: + en: Writing new key +You are not logged in: + en: You are not logged in +You have logged out!: + en: You have logged out! +_version: 2 +bug in list query. please report: + en: 'bug in list query. please report' +bug in search query. please report: + en: bug in search query. please report +cannot decrypt history from a newer version of atuin: + en: cannot decrypt history from a newer version of atuin +could not decode encryption key: + en: could not decode encryption key +could not decode provided key - is not valid base64: + en: could not decode provided key - is not valid base64 +could not decrypt history: + en: could not decrypt history +could not encode key to message pack: + en: could not encode key to message pack +could not encrypt: + en: could not encrypt +could not register user due to version mismatch: + en: could not register user due to version mismatch +could not sync due to version mismatch: + en: could not sync due to version mismatch +could not sync records due to version mismatch: + en: could not sync records due to version mismatch +current password is incorrect: + en: current password is incorrect +encryption key is not a valid base64 encoding: + en: encryption key is not a valid base64 encoding +encryption key is not the correct size: + en: encryption key is not the correct size +expected an error decrypting with invalid key: + en: expected an error decrypting with invalid key +failed to get count (are you logged in?): + en: failed to get count (are you logged in?) +failed to load host ID: + en: failed to load host ID +failed to parse server version: + en: failed to parse server version +got remote index: + en: got remote index +invalid login details: + en: invalid login details +issue in stats average query: + en: issue in stats average query +issue in stats day of week query: + en: issue in stats day of week query +issue in stats duration over time query: + en: issue in stats duration over time query +issue in stats exits query: + en: issue in stats exits query +issue in stats next query: + en: issue in stats next query +issue in stats previous query: + en: issue in stats previous query +key already exists! cannot overwrite: + en: key already exists! cannot overwrite +key mnemonic was not valid: + en: key mnemonic was not valid +key was not the correct length: + en: key was not the correct length +'kv value too large: max len {len} bytes': + en: 'kv value too large: max len {len} bytes' +listing history: + en: listing history +'listing history from %{from:?} to %{to:?}': + en: 'listing history from %{from:?} to %{to:?}' +loading history item: + en: loading history item +malformed decrypted history: + en: malformed decrypted history +saving history to sqlite: + en: saving history to sqlite +too many entries in v0 kv record: + en: too many entries in v0 kv record +trailing bytes in encoded history. malformed: + en: 'trailing bytes in encoded history. malformed' +trailing bytes in encoded kvrecord. malformed: + en: 'trailing bytes in encoded kvrecord. malformed' +unknown version: + en: unknown version +'unknown version %{version:?}': + en: 'unknown version %{version:?}' +updating sqlite history: + en: updating sqlite history +'uploading %{records} records to %{url}': + en: 'uploading %{records} records to %{url}' +username already in use: + en: username already in use diff --git a/crates/atuin-common/locales/TODO.yml b/crates/atuin-common/locales/TODO.yml new file mode 100644 index 00000000000..98890b0283a --- /dev/null +++ b/crates/atuin-common/locales/TODO.yml @@ -0,0 +1,22 @@ +$HOME not found: + en: $HOME not found +'%userprofile% not found': + en: '%userprofile% not found' +Atuin running with no parent!: + en: Atuin running with no parent! +Failed to generate random bytes!: + en: Failed to generate random bytes! +Failed to get current PID: + en: Failed to get current PID +Process with current pid does not exist: + en: Process with current pid does not exist +Process with parent pid does not exist: + en: Process with parent pid does not exist +_version: 2 +failed to parse self semver: + en: failed to parse self semver +not enough chars: + en: not enough chars +unexpected eof, quotes do not match: + en: unexpected eof, quotes do not match + diff --git a/crates/atuin/locales/TODO.yml b/crates/atuin/locales/TODO.yml new file mode 100644 index 00000000000..b728b8f5ed2 --- /dev/null +++ b/crates/atuin/locales/TODO.yml @@ -0,0 +1,4 @@ +_version: 2 +could not load client settings: + en: could not load client settings + From 287bd1b43a65d9434763b34409fd5b3e77cad594 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 21:02:33 +0000 Subject: [PATCH 08/16] fix(i18n): fix up mismatched t! usage --- crates/atuin-client/src/database.rs | 2 +- crates/atuin-client/src/encryption.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/atuin-client/src/database.rs b/crates/atuin-client/src/database.rs index 95f039454a7..893db15bffe 100644 --- a/crates/atuin-client/src/database.rs +++ b/crates/atuin-client/src/database.rs @@ -292,7 +292,7 @@ impl Database for Sqlite { unique: bool, include_deleted: bool, ) -> Result> { - debug!(t!("listing history")); + debug!("{}", t!("listing history")); let mut query = SqlBuilder::select_from(SqlName::new("history").alias("h").baquoted()); query.field("*").order_desc("timestamp"); diff --git a/crates/atuin-client/src/encryption.rs b/crates/atuin-client/src/encryption.rs index d1c544633f6..1da85c295a8 100644 --- a/crates/atuin-client/src/encryption.rs +++ b/crates/atuin-client/src/encryption.rs @@ -305,7 +305,7 @@ mod test { }; // this should err - let _ = decrypt(e2, &key1).expect_err(t!("expected an error decrypting with invalid key")); + let _ = decrypt(e2, &key1).expect_err(&t!("expected an error decrypting with invalid key")); } #[test] From 57b8e412a52dbef105bec452dbbfddc96843a96b Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 22:24:47 +0000 Subject: [PATCH 09/16] fix(i18n): add in missing translations for client and common --- crates/atuin-client/locales/TODO.yml | 191 +++++++++++++++++- crates/atuin-client/locales/app.yml | 3 +- crates/atuin-client/src/database.rs | 2 +- crates/atuin-client/src/history.rs | 8 +- crates/atuin-client/src/history/store.rs | 23 ++- crates/atuin-client/src/import/bash.rs | 4 +- crates/atuin-client/src/import/fish.rs | 4 +- crates/atuin-client/src/import/mod.rs | 2 +- crates/atuin-client/src/import/nu.rs | 4 +- crates/atuin-client/src/import/nu_histdb.rs | 4 +- crates/atuin-client/src/import/replxx.rs | 2 +- crates/atuin-client/src/import/resh.rs | 2 +- crates/atuin-client/src/import/xonsh.rs | 4 +- .../atuin-client/src/import/xonsh_sqlite.rs | 15 +- crates/atuin-client/src/import/zsh.rs | 6 +- crates/atuin-client/src/import/zsh_histdb.rs | 4 +- crates/atuin-client/src/record/encryption.rs | 18 +- .../atuin-client/src/record/sqlite_store.rs | 27 +-- crates/atuin-client/src/record/sync.rs | 50 +++-- crates/atuin-client/src/settings.rs | 34 ++-- crates/atuin-client/src/sync.rs | 23 ++- crates/atuin-client/src/theme.rs | 25 ++- crates/atuin-common/locales/TODO.yml | 2 - crates/atuin-common/locales/app.yml | 8 + crates/atuin-common/src/lib.rs | 2 +- crates/atuin-common/src/utils.rs | 41 ++++ 26 files changed, 376 insertions(+), 132 deletions(-) create mode 100644 crates/atuin-common/locales/app.yml diff --git a/crates/atuin-client/locales/TODO.yml b/crates/atuin-client/locales/TODO.yml index 2e435034956..85f88b18cac 100644 --- a/crates/atuin-client/locales/TODO.yml +++ b/crates/atuin-client/locales/TODO.yml @@ -1,17 +1,72 @@ +_version: 2 Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin: en: Atuin version mismatch! In order to successfully sync, the server needs to run a newer version of Atuin Client: en: Client +Converting old history to new store: + en: Converting old history to new store +Could not build config: + en: Could not build config +Could not build default: + en: Could not build default +Could not convert color name %{name} to Crossterm color: + en: Could not convert color name %{name} to Crossterm color +Could not deserialize config: + en: Could not deserialize config +Could not determine home directory: + en: Could not determine home directory +'Could not find history file %{histfile}. Try setting $HISTFILE': + en: Could not find history file %{histfile}. Try setting $HISTFILE +Could not find history file.: + en: 'Could not find history file.' +Could not find history file. Try setting $HISTDB_FILE: + en: 'Could not find history file. Try setting $HISTDB_FILE' +Could not find history file. Try setting and exporting $HISTFILE: + en: 'Could not find history file. Try setting and exporting $HISTFILE' +'Could not find xonsh history db at: %{hist_file}': + en: 'Could not find xonsh history db at: %{hist_file}' +Could not find xonsh history files: + en: Could not find xonsh history files +'Could not load theme %{theme}': + en: 'Could not load theme %{theme}' Could not login due to version mismatch: en: Could not login due to version mismatch +Could not parse 3 hex values from string: + en: Could not parse 3 hex values from string +Could not write host ID to data dir: + en: Could not write host ID to data dir +Downloaded records: + en: Downloaded records +'Downloading %{records} records from %{host}/%{tag}': + en: 'Downloading %{records} records from %{host}/%{tag}' 'ERROR: Failed to find $ATUIN_SESSION in the environment. Check that you have correctly set up your shell.': en: 'ERROR: Failed to find $ATUIN_SESSION in the environment. Check that you have correctly set up your shell.' +Empty theme directory override and could not find theme elsewhere: + en: Empty theme directory override and could not find theme elsewhere Failed to build secrets regex: en: Failed to build secrets regex +Failed to decrypt %{record}, deleting: + en: Failed to decrypt %{record}, deleting +Failed to deserialize theme: + en: Failed to deserialize theme Failed to remove session file: en: Failed to remove session file +Fetching history already in store: + en: Fetching history already in store +Fetching history from old database: + en: Fetching history from old database +Import complete: + en: Import complete +Invalid path for SQLite database: + en: Invalid path for SQLite database Invalid request to the service: en: Invalid request to the service +'Loaded: %{count}': + en: 'Loaded: %{count}' +No such color in palette: + en: No such color in palette +Parent requested but we hit the recursion limit: + en: Parent requested but we hit the recursion limit Rate limited; please wait before doing that again: en: Rate limited; please wait before doing that again Re-encrypting local store with new key: @@ -26,53 +81,140 @@ Server: en: 'There was an error with the atuin sync service, server error %{status}: %{reason}. If the problem persists, contact the host' 'There was an error with the atuin sync service: Status %{status:?}. If the problem persists, contact the host': en: 'There was an error with the atuin sync service: Status %{status:?}. If the problem persists, contact the host' +Time reversal detected in Bash history! Commands may be ordered incorrectly.: + en: 'Time reversal detected in Bash history! Commands may be ordered incorrectly.' +Tried to load session; not logged in: + en: Tried to load session; not logged in +Tried to load string as a color unsuccessfully: + en: Tried to load string as a color unsuccessfully Unknown error: en: Unknown error +Uploaded records: + en: Uploaded records +Uploading %{records} records to %{host}/%{tag}: + en: Uploading %{records} records to %{host}/%{tag} Writing new key: en: Writing new key +Writing to db: + en: Writing to db You are not logged in: en: You are not logged in You have logged out!: en: You have logged out! -_version: 2 +Your theme config name is not the name of your loaded theme: + en: Your theme config name is not the name of your loaded theme +a request to the sync server failed: + en: a request to the sync server failed +an error occurred: + en: an error occurred +an issue with the local database occurred: + en: an issue with the local database occurred +attempting to decrypt with incorrect key. currently using %{current_kid}, expecting %{kid}: + en: 'attempting to decrypt with incorrect key. currently using %{current_kid}, expecting %{kid}' bug in list query. please report: en: 'bug in list query. please report' bug in search query. please report: - en: bug in search query. please report + en: 'bug in list query. please report' +cannot decrypt history from a different version of Atuin: + en: cannot decrypt history from a different version of Atuin cannot decrypt history from a newer version of atuin: en: cannot decrypt history from a newer version of atuin +could not create config file: + en: could not create config file +could not create dir %{config_dir}: + en: could not create dir %{config_dir} +could not create dir %{data_dir}: + en: could not create dir %{data_dir} could not decode encryption key: en: could not decode encryption key could not decode provided key - is not valid base64: en: could not decode provided key - is not valid base64 +could not decrypt entry: + en: could not decrypt entry could not decrypt history: en: could not decrypt history +could not delete history with id %{id}, not found locally: + en: could not delete history with id %{id}, not found locally +could not determine data directory: + en: could not determine data directory could not encode key to message pack: en: could not encode key to message pack could not encrypt: en: could not encrypt +could not find user directories: + en: could not find user directories +could not get last element of page: + en: could not get last element of page could not register user due to version mismatch: en: could not register user due to version mismatch +could not serialize implicit assertions: + en: could not serialize implicit assertions +could not serialize wrapped cek: + en: could not serialize wrapped cek +could not source from random: + en: could not source from random could not sync due to version mismatch: en: could not sync due to version mismatch could not sync records due to version mismatch: en: could not sync records due to version mismatch +could not write default config file: + en: could not write default config file current password is incorrect: en: current password is incorrect +deleting %{id} on remote: + en: deleting %{id} on remote +diff has nothing for local or remote - (host, tag) does not exist: + en: diff has nothing for local or remote - (host, tag) does not exist encryption key is not a valid base64 encoding: en: encryption key is not a valid base64 encoding encryption key is not the correct size: en: encryption key is not the correct size +error encrypting atuin data: + en: error encrypting atuin data expected an error decrypting with invalid key: en: expected an error decrypting with invalid key +expected decoding v0 record, found v%{version}: + en: expected decoding v0 record, found v%{version} +failed to check sync: + en: failed to check sync +failed to create client: + en: failed to create client +failed to decrypt history! check your key: + en: failed to decrypt history! check your key +failed to deserialize: + en: failed to deserialize +failed to fetch local store len: + en: failed to fetch local store len +failed to fetch local store status: + en: failed to fetch local store status failed to get count (are you logged in?): en: failed to get count (are you logged in?) failed to load host ID: en: failed to load host ID +failed to open in memory sqlite: + en: failed to open in memory sqlite +failed to override timezone with UTC: + en: failed to override timezone with UTC +failed to parse host ID from local directory: + en: failed to parse host ID from local directory failed to parse server version: en: failed to parse server version +failed to parse uuid for local store status: + en: failed to parse uuid for local store status +failed to post records: + en: failed to post records +failed to read upload page: + en: failed to read upload page +file task panicked: + en: file task panicked got remote index: en: got remote index +invalid base64: + en: invalid base64 +invalid host UUID format in sqlite DB: + en: invalid host UUID format in sqlite DB +invalid id UUID format in sqlite DB: + en: invalid id UUID format in sqlite DB invalid login details: en: invalid login details issue in stats average query: @@ -97,27 +239,60 @@ key was not the correct length: en: 'kv value too large: max len {len} bytes' listing history: en: listing history -'listing history from %{from:?} to %{to:?}': - en: 'listing history from %{from:?} to %{to:?}' +listing history from %{from} to %{to}: + en: listing history from %{from} to %{to} loading history item: en: loading history item malformed decrypted history: en: malformed decrypted history +opening sqlite database at %{path}: + en: opening sqlite database at %{path} +operational error: + en: operational error +remote has %{remote_count}, we have %{local_count}: + en: remote has %{remote_count}, we have %{local_count} +running sqlite database setup: + en: running sqlite database setup saving history to sqlite: en: saving history to sqlite +set theme debug on for more info: + en: set theme debug on for more info +skipping {id} - already exists: + en: skipping {id} - already exists +something has gone wrong with the sync logic: + en: something has gone wrong with the sync logic +starting sync download: + en: starting sync download +sync downloaded %{num}: + en: sync downloaded %{num} +the local store is ahead of the remote, but for another host. has remote lost data?: + en: 'the local store is ahead of the remote, but for another host. has remote lost data?' too many entries in v0 kv record: en: too many entries in v0 kv record +trailing bytes decoding HistoryRecord::Delete - malformed? got %{bytes}: + en: trailing bytes decoding HistoryRecord::Delete - malformed? got %{bytes} trailing bytes in encoded history. malformed: en: 'trailing bytes in encoded history. malformed' trailing bytes in encoded kvrecord. malformed: en: 'trailing bytes in encoded kvrecord. malformed' +unknown HistoryRecord type %{n}: + en: unknown HistoryRecord type %{n} +unknown history version %{version}: + en: unknown history version %{version} unknown version: en: unknown version -'unknown version %{version:?}': - en: 'unknown version %{version:?}' +unknown version %{version:?}: + en: unknown version %{version:?} +unknown version %{version}: + en: unknown version %{version} updating sqlite history: en: updating sqlite history -'uploading %{records} records to %{url}': - en: 'uploading %{records} records to %{url}' +upload cursor: + en: upload cursor +uploading %{records} records to %{url}: + en: uploading %{records} records to %{url} username already in use: en: username already in use +wrapped cek did not contain the correct contents: + en: wrapped cek did not contain the correct contents + diff --git a/crates/atuin-client/locales/app.yml b/crates/atuin-client/locales/app.yml index 707b1753d2d..fe124e7f637 100644 --- a/crates/atuin-client/locales/app.yml +++ b/crates/atuin-client/locales/app.yml @@ -1,3 +1,4 @@ +_version: 2 DIRECTORY: en: DIRECTORY ga: COMHADLANN @@ -13,5 +14,3 @@ SESSION: WORKSPACE: en: WORKSPACE ga: SPÁS OIBRE -_version: 2 - diff --git a/crates/atuin-client/src/database.rs b/crates/atuin-client/src/database.rs index 893db15bffe..7ec98bb442e 100644 --- a/crates/atuin-client/src/database.rs +++ b/crates/atuin-client/src/database.rs @@ -335,7 +335,7 @@ impl Database for Sqlite { } async fn range(&self, from: OffsetDateTime, to: OffsetDateTime) -> Result> { - debug!("{}", t!("listing history from %{from:?} to %{to:?}", from=from, to=to)); + debug!("{}", t!("listing history from %{from} to %{to}", from=format!("{from:?}"), to=format!("{to:?}"))); let res = sqlx::query( "select * from history where timestamp >= ?1 and timestamp <= ?2 order by timestamp asc", diff --git a/crates/atuin-client/src/history.rs b/crates/atuin-client/src/history.rs index 19a26a7e075..6738f557935 100644 --- a/crates/atuin-client/src/history.rs +++ b/crates/atuin-client/src/history.rs @@ -164,13 +164,13 @@ impl History { let version = decode::read_u16(&mut bytes).map_err(error_report)?; if version != 0 { - bail!("expected decoding v0 record, found v{version}"); + bail!(t!("expected decoding v0 record, found v%{version}", version=version)); } let nfields = decode::read_array_len(&mut bytes).map_err(error_report)?; if nfields != 9 { - bail!("cannot decrypt history from a different version of Atuin"); + bail!(t!("cannot decrypt history from a different version of Atuin")); } let bytes = bytes.remaining_slice(); @@ -198,7 +198,7 @@ impl History { }; if !bytes.is_empty() { - bail!("trailing bytes in encoded history. malformed") + bail!(t!("trailing bytes in encoded history. malformed")) } Ok(History { @@ -220,7 +220,7 @@ impl History { match version { HISTORY_VERSION => Self::deserialize_v0(bytes), - _ => bail!("unknown version {version:?}"), + _ => bail!(t!("unknown version %{version}", version=format!("{:?}", version))), } } diff --git a/crates/atuin-client/src/history/store.rs b/crates/atuin-client/src/history/store.rs index b0341a2652b..b29e4add240 100644 --- a/crates/atuin-client/src/history/store.rs +++ b/crates/atuin-client/src/history/store.rs @@ -93,16 +93,17 @@ impl HistoryRecord { let (id, bytes) = decode::read_str_from_slice(bytes).map_err(error_report)?; if !bytes.is_empty() { - bail!( - "trailing bytes decoding HistoryRecord::Delete - malformed? got {bytes:?}" - ); + bail!(t!( + "trailing bytes decoding HistoryRecord::Delete - malformed? got %{bytes}", + bytes=format!("{bytes:?}") + )); } Ok(HistoryRecord::Delete(id.to_string().into())) } n => { - bail!("unknown HistoryRecord type {n}") + bail!(t!("unknown HistoryRecord type %{n}", n=n)) } } } @@ -201,7 +202,7 @@ impl HistoryStore { HistoryRecord::deserialize(&decrypted.data, HISTORY_VERSION) } - version => bail!("unknown history version {version:?}"), + version => bail!(t!("unknown history version %{version}", version=format!("{version:?}"))), }?; ret.push(hist); @@ -299,22 +300,22 @@ impl HistoryStore { ); pb.enable_steady_tick(Duration::from_millis(500)); - pb.set_message("Fetching history from old database"); + pb.set_message(t!("Fetching history from old database")); let context = current_context(); let history = db.list(&[], &context, None, false, true).await?; - pb.set_message("Fetching history already in store"); + pb.set_message(t!("Fetching history already in store")); let store_ids = self.history_ids().await?; - pb.set_message("Converting old history to new store"); + pb.set_message(t!("Converting old history to new store")); let mut records = Vec::new(); for i in history { debug!("loaded {}", i.id); if store_ids.contains(&i.id) { - debug!("skipping {} - already exists", i.id); + debug!("{}", t!("skipping {id} - already exists", id=i.id)); continue; } @@ -325,13 +326,13 @@ impl HistoryStore { } } - pb.set_message("Writing to db"); + pb.set_message(t!("Writing to db")); if !records.is_empty() { self.push_batch(records.into_iter()).await?; } - pb.finish_with_message("Import complete"); + pb.finish_with_message(t!("Import complete")); Ok(()) } diff --git a/crates/atuin-client/src/import/bash.rs b/crates/atuin-client/src/import/bash.rs index ade1f751f84..2c5b6f3c3e0 100644 --- a/crates/atuin-client/src/import/bash.rs +++ b/crates/atuin-client/src/import/bash.rs @@ -16,7 +16,7 @@ pub struct Bash { } fn default_histpath() -> Result { - let user_dirs = UserDirs::new().ok_or_else(|| eyre!("could not find user directories"))?; + let user_dirs = UserDirs::new().ok_or_else(|| eyre!(t!("could not find user directories")))?; let home_dir = user_dirs.home_dir(); Ok(home_dir.join(".bash_history")) @@ -73,7 +73,7 @@ impl Importer for Bash { LineType::Empty => {} // do nothing LineType::Timestamp(t) => { if t < next_timestamp { - warn!("Time reversal detected in Bash history! Commands may be ordered incorrectly."); + warn!("{}", t!("Time reversal detected in Bash history! Commands may be ordered incorrectly.")); } next_timestamp = t; } diff --git a/crates/atuin-client/src/import/fish.rs b/crates/atuin-client/src/import/fish.rs index 714b2d01230..213b4e96917 100644 --- a/crates/atuin-client/src/import/fish.rs +++ b/crates/atuin-client/src/import/fish.rs @@ -19,7 +19,7 @@ pub struct Fish { /// see https://fishshell.com/docs/current/interactive.html#searchable-command-history fn default_histpath() -> Result { - let base = BaseDirs::new().ok_or_else(|| eyre!("could not determine data directory"))?; + let base = BaseDirs::new().ok_or_else(|| eyre!(t!("could not determine data directory")))?; let data = std::env::var("XDG_DATA_HOME").map_or_else( |_| base.home_dir().join(".local").join("share"), PathBuf::from, @@ -40,7 +40,7 @@ fn default_histpath() -> Result { if histpath.exists() { Ok(histpath) } else { - Err(eyre!("Could not find history file.")) + Err(eyre!(t!("Could not find history file."))) } } diff --git a/crates/atuin-client/src/import/mod.rs b/crates/atuin-client/src/import/mod.rs index eb3ce04511c..bef01c7ad10 100644 --- a/crates/atuin-client/src/import/mod.rs +++ b/crates/atuin-client/src/import/mod.rs @@ -89,7 +89,7 @@ fn is_file(p: PathBuf) -> Result { if p.is_file() { Ok(p) } else { - bail!("Could not find history file {:?}. Try setting $HISTFILE", p) + bail!(t!("Could not find history file %{histfile}. Try setting $HISTFILE", histfile=format!("{p:?}"))) } } diff --git a/crates/atuin-client/src/import/nu.rs b/crates/atuin-client/src/import/nu.rs index a45d83c5ee9..b9340ad7df2 100644 --- a/crates/atuin-client/src/import/nu.rs +++ b/crates/atuin-client/src/import/nu.rs @@ -18,14 +18,14 @@ pub struct Nu { } fn get_histpath() -> Result { - let base = BaseDirs::new().ok_or_else(|| eyre!("could not determine data directory"))?; + let base = BaseDirs::new().ok_or_else(|| eyre!(t!("could not determine data directory")))?; let config_dir = base.config_dir().join("nushell"); let histpath = config_dir.join("history.txt"); if histpath.exists() { Ok(histpath) } else { - Err(eyre!("Could not find history file.")) + Err(eyre!(t!("Could not find history file."))) } } diff --git a/crates/atuin-client/src/import/nu_histdb.rs b/crates/atuin-client/src/import/nu_histdb.rs index f0e8e95c234..15f92a889f8 100644 --- a/crates/atuin-client/src/import/nu_histdb.rs +++ b/crates/atuin-client/src/import/nu_histdb.rs @@ -73,14 +73,14 @@ async fn hist_from_db_conn(pool: Pool) -> Result> impl NuHistDb { pub fn histpath() -> Result { - let base = BaseDirs::new().ok_or_else(|| eyre!("could not determine data directory"))?; + let base = BaseDirs::new().ok_or_else(|| eyre!(t!("could not determine data directory")))?; let config_dir = base.config_dir().join("nushell"); let histdb_path = config_dir.join("history.sqlite3"); if histdb_path.exists() { Ok(histdb_path) } else { - Err(eyre!("Could not find history file.")) + Err(eyre!(t!("Could not find history file."))) } } } diff --git a/crates/atuin-client/src/import/replxx.rs b/crates/atuin-client/src/import/replxx.rs index b73522ce494..61008f49608 100644 --- a/crates/atuin-client/src/import/replxx.rs +++ b/crates/atuin-client/src/import/replxx.rs @@ -15,7 +15,7 @@ pub struct Replxx { } fn default_histpath() -> Result { - let user_dirs = UserDirs::new().ok_or_else(|| eyre!("could not find user directories"))?; + let user_dirs = UserDirs::new().ok_or_else(|| eyre!(t!("could not find user directories")))?; let home_dir = user_dirs.home_dir(); // There is no default histfile for replxx. diff --git a/crates/atuin-client/src/import/resh.rs b/crates/atuin-client/src/import/resh.rs index 396d11fdfb1..dd9a865379d 100644 --- a/crates/atuin-client/src/import/resh.rs +++ b/crates/atuin-client/src/import/resh.rs @@ -74,7 +74,7 @@ pub struct Resh { } fn default_histpath() -> Result { - let user_dirs = UserDirs::new().ok_or_else(|| eyre!("could not find user directories"))?; + let user_dirs = UserDirs::new().ok_or_else(|| eyre!(t!("could not find user directories")))?; let home_dir = user_dirs.home_dir(); Ok(home_dir.join(".resh_history.json")) diff --git a/crates/atuin-client/src/import/xonsh.rs b/crates/atuin-client/src/import/xonsh.rs index 19ce4cf68d6..cd644ece658 100644 --- a/crates/atuin-client/src/import/xonsh.rs +++ b/crates/atuin-client/src/import/xonsh.rs @@ -51,13 +51,13 @@ fn xonsh_hist_dir(xonsh_data_dir: Option) -> Result { } // otherwise, fall back to default - let base = BaseDirs::new().ok_or_else(|| eyre!("Could not determine home directory"))?; + let base = BaseDirs::new().ok_or_else(|| eyre!(t!("Could not determine home directory")))?; let hist_dir = base.data_dir().join("xonsh/history_json"); if hist_dir.exists() || cfg!(test) { Ok(hist_dir) } else { - Err(eyre!("Could not find xonsh history files")) + Err(eyre!(t!("Could not find xonsh history files"))) } } diff --git a/crates/atuin-client/src/import/xonsh_sqlite.rs b/crates/atuin-client/src/import/xonsh_sqlite.rs index 2817dc630fa..e6333d226c0 100644 --- a/crates/atuin-client/src/import/xonsh_sqlite.rs +++ b/crates/atuin-client/src/import/xonsh_sqlite.rs @@ -67,16 +67,16 @@ fn xonsh_db_path(xonsh_data_dir: Option) -> Result { } // otherwise, fall back to default - let base = BaseDirs::new().ok_or_else(|| eyre!("Could not determine home directory"))?; + let base = BaseDirs::new().ok_or_else(|| eyre!(t!("Could not determine home directory")))?; let hist_file = base.data_dir().join("xonsh/xonsh-history.sqlite"); if hist_file.exists() || cfg!(test) { Ok(hist_file) } else { - Err(eyre!( - "Could not find xonsh history db at: {}", - hist_file.to_string_lossy() - )) + Err(eyre!(t!( + "Could not find xonsh history db at: %{hist_file}", + hist_file=hist_file.to_string_lossy() + ))) } } @@ -96,7 +96,8 @@ impl Importer for XonshSqlite { let db_path = get_histpath(|| xonsh_db_path(xonsh_data_dir))?; let connection_str = db_path.to_str().ok_or_else(|| { eyre!( - "Invalid path for SQLite database: {}", + "{}: {}", + t!("Invalid path for SQLite database"), db_path.to_string_lossy() ) })?; @@ -130,7 +131,7 @@ impl Importer for XonshSqlite { count += 1; } - println!("Loaded: {count}"); + println!("{}", t!("Loaded: %{count}", count=count)); Ok(()) } } diff --git a/crates/atuin-client/src/import/zsh.rs b/crates/atuin-client/src/import/zsh.rs index 5bc8fc16416..3948e58a2da 100644 --- a/crates/atuin-client/src/import/zsh.rs +++ b/crates/atuin-client/src/import/zsh.rs @@ -22,7 +22,7 @@ fn default_histpath() -> Result { // oh-my-zsh sets HISTFILE=~/.zhistory // zsh has no default value for this var, but uses ~/.zhistory. // we could maybe be smarter about this in the future :) - let user_dirs = UserDirs::new().ok_or_else(|| eyre!("could not find user directories"))?; + let user_dirs = UserDirs::new().ok_or_else(|| eyre!(t!("could not find user directories")))?; let home_dir = user_dirs.home_dir(); let mut candidates = [".zhistory", ".zsh_history"].iter(); @@ -35,9 +35,9 @@ fn default_histpath() -> Result { } } None => { - break Err(eyre!( + break Err(eyre!(t!( "Could not find history file. Try setting and exporting $HISTFILE" - )) + ))) } } } diff --git a/crates/atuin-client/src/import/zsh_histdb.rs b/crates/atuin-client/src/import/zsh_histdb.rs index eb72baa3d2b..c0e746117e7 100644 --- a/crates/atuin-client/src/import/zsh_histdb.rs +++ b/crates/atuin-client/src/import/zsh_histdb.rs @@ -111,9 +111,9 @@ impl ZshHistDb { if histdb_path.exists() { Ok(histdb_path) } else { - Err(eyre!( + Err(eyre!(t!( "Could not find history file. Try setting $HISTDB_FILE" - )) + ))) } } } diff --git a/crates/atuin-client/src/record/encryption.rs b/crates/atuin-client/src/record/encryption.rs index 3ad3be664c9..f91ec519b0c 100644 --- a/crates/atuin-client/src/record/encryption.rs +++ b/crates/atuin-client/src/record/encryption.rs @@ -76,14 +76,14 @@ impl Encryption for PASETO_V4 { data: general_purpose::URL_SAFE_NO_PAD.encode(data.0), }) .expect("json encoding can't fail"); - let nonce = DataKey::<32>::try_new_random().expect("could not source from random"); + let nonce = DataKey::<32>::try_new_random().expect(&t!("could not source from random")); let nonce = PasetoNonce::::from(&nonce); let token = Paseto::::builder() .set_payload(Payload::from(payload.as_str())) .set_implicit_assertion(ImplicitAssertion::from(assertions.as_str())) .try_encrypt(&random_key.into(), &nonce) - .expect("error encrypting atuin data"); + .expect(&t!("error encrypting atuin data")); EncryptedData { data: token, @@ -105,7 +105,7 @@ impl Encryption for PASETO_V4 { None, ImplicitAssertion::from(&*assertions), ) - .context("could not decrypt entry")?; + .context(t!("could not decrypt entry"))?; let payload: AtuinPayload = serde_json::from_str(&payload)?; let data = general_purpose::URL_SAFE_NO_PAD.decode(payload.data)?; @@ -120,7 +120,7 @@ impl PASETO_V4 { // let wrapping_key = PasetoSymmetricKey::from(Key::from(key)); let AtuinFooter { kid, wpk } = serde_json::from_str(&wrapped_cek) - .context("wrapped cek did not contain the correct contents")?; + .context(t!("wrapped cek did not contain the correct contents"))?; // check that the wrapping key matches the required key to decrypt. // In future, we could support multiple keys and use this key to @@ -131,7 +131,11 @@ impl PASETO_V4 { ensure!( current_kid == kid, - "attempting to decrypt with incorrect key. currently using {current_kid}, expecting {kid}" + t!( + "attempting to decrypt with incorrect key. currently using %{current_kid}, expecting %{kid}", + current_kid=current_kid, + kid=kid + ) ); // decrypt the random key @@ -147,7 +151,7 @@ impl PASETO_V4 { wpk: cek.wrap_pie(&wrapping_key), kid: wrapping_key.to_id(), }; - serde_json::to_string(&wrapped_cek).expect("could not serialize wrapped cek") + serde_json::to_string(&wrapped_cek).expect(&t!("could not serialize wrapped cek")) } } @@ -191,7 +195,7 @@ impl<'a> From> for Assertions<'a> { impl Assertions<'_> { fn encode(&self) -> String { - serde_json::to_string(self).expect("could not serialize implicit assertions") + serde_json::to_string(self).expect(&t!("could not serialize implicit assertions")) } } diff --git a/crates/atuin-client/src/record/sqlite_store.rs b/crates/atuin-client/src/record/sqlite_store.rs index 2937dbd7310..e3a8fec1d2e 100644 --- a/crates/atuin-client/src/record/sqlite_store.rs +++ b/crates/atuin-client/src/record/sqlite_store.rs @@ -31,7 +31,7 @@ impl SqliteStore { pub async fn new(path: impl AsRef, timeout: f64) -> Result { let path = path.as_ref(); - debug!("opening sqlite database at {:?}", path); + debug!("{}", t!("opening sqlite database at %{path}", path=format!("{path:?}"))); let create = !path.exists(); if create { @@ -56,7 +56,7 @@ impl SqliteStore { } async fn setup_db(pool: &SqlitePool) -> Result<()> { - debug!("running sqlite database setup"); + debug!("{}", t!("running sqlite database setup")); sqlx::migrate!("./record-migrations").run(pool).await?; @@ -91,8 +91,8 @@ impl SqliteStore { let timestamp: i64 = row.get("timestamp"); // tbh at this point things are pretty fucked so just panic - let id = Uuid::from_str(row.get("id")).expect("invalid id UUID format in sqlite DB"); - let host = Uuid::from_str(row.get("host")).expect("invalid host UUID format in sqlite DB"); + let id = Uuid::from_str(row.get("id")).expect(&t!("invalid id UUID format in sqlite DB")); + let host = Uuid::from_str(row.get("host")).expect(&t!("invalid host UUID format in sqlite DB")); Record { id: RecordId(id), @@ -171,7 +171,7 @@ impl Store for SqliteStore { match res { Err(sqlx::Error::RowNotFound) => Ok(None), - Err(e) => Err(eyre!("an error occurred: {}", e)), + Err(e) => Err(eyre!("{}: {}", t!("an error occurred"), e)), Ok(record) => Ok(Some(record)), } } @@ -185,7 +185,7 @@ impl Store for SqliteStore { .fetch_one(&self.pool) .await; match res { - Err(e) => Err(eyre!("failed to fetch local store len: {}", e)), + Err(e) => Err(eyre!("{}: {}", t!("failed to fetch local store len"), e)), Ok(v) => Ok(v.0 as u64), } } @@ -197,7 +197,7 @@ impl Store for SqliteStore { .fetch_one(&self.pool) .await; match res { - Err(e) => Err(eyre!("failed to fetch local store len: {}", e)), + Err(e) => Err(eyre!("{}: {}", t!("failed to fetch local store len"), e)), Ok(v) => Ok(v.0 as u64), } } @@ -249,7 +249,7 @@ impl Store for SqliteStore { match res { Err(sqlx::Error::RowNotFound) => Ok(None), - Err(e) => Err(eyre!("an error occurred: {}", e)), + Err(e) => Err(eyre!("{}: {}", t!("an error occurred"), e)), Ok(v) => Ok(Some(v)), } } @@ -263,13 +263,13 @@ impl Store for SqliteStore { .await; let res = match res { - Err(e) => return Err(eyre!("failed to fetch local store status: {}", e)), + Err(e) => return Err(eyre!("{}: {}", t!("failed to fetch local store status"), e)), Ok(v) => v, }; for i in res { let host = HostId( - Uuid::from_str(i.0.as_str()).expect("failed to parse uuid for local store status"), + Uuid::from_str(i.0.as_str()).expect(&t!("failed to parse uuid for local store status")), ); status.set_raw(host, i.1, i.2 as u64); @@ -349,8 +349,11 @@ impl Store for SqliteStore { Ok(_) => continue, Err(_) => { println!( - "Failed to decrypt {}, deleting", - record.id.0.as_hyphenated() + "{}", + t!( + "Failed to decrypt %{record}, deleting", + record=record.id.0.as_hyphenated() + ) ); self.delete(record.id).await?; diff --git a/crates/atuin-client/src/record/sync.rs b/crates/atuin-client/src/record/sync.rs index e8d0fbf7943..62b2b0bfb00 100644 --- a/crates/atuin-client/src/record/sync.rs +++ b/crates/atuin-client/src/record/sync.rs @@ -12,19 +12,19 @@ use indicatif::{ProgressBar, ProgressState, ProgressStyle}; #[derive(Error, Debug)] pub enum SyncError { - #[error("the local store is ahead of the remote, but for another host. has remote lost data?")] + #[error("{}", t!("the local store is ahead of the remote, but for another host. has remote lost data?"))] LocalAheadOtherHost, - #[error("an issue with the local database occurred: {msg:?}")] + #[error("{}: {msg:?}", t!("an issue with the local database occurred"))] LocalStoreError { msg: String }, - #[error("something has gone wrong with the sync logic: {msg:?}")] + #[error("{}: {msg:?}", t!("something has gone wrong with the sync logic"))] SyncLogicError { msg: String }, - #[error("operational error: {msg:?}")] + #[error("{}: {msg:?}", t!("operational error"))] OperationalError { msg: String }, - #[error("a request to the sync server failed: {msg:?}")] + #[error("{}: {msg:?}", t!("a request to the sync server failed"))] RemoteRequestError { msg: String }, } @@ -130,9 +130,9 @@ pub async fn operations( // something is pretty fucked. (None, None) => { return Err(SyncError::SyncLogicError { - msg: String::from( + msg: t!( "diff has nothing for local or remote - (host, tag) does not exist", - ), + ).to_string(), }) } }; @@ -176,10 +176,13 @@ async fn sync_upload( .progress_chars("#>-")); println!( - "Uploading {} records to {}/{}", - expected, - host.0.as_simple(), - tag + "{}", + t!( + "Uploading %{records} records to %{host}/%{tag}", + records=expected, + host=host.0.as_simple(), + tag=tag + ) ); // preload with the first entry if remote does not know of this store @@ -188,13 +191,13 @@ async fn sync_upload( .next(host, tag.as_str(), remote + progress, upload_page_size) .await .map_err(|e| { - error!("failed to read upload page: {e:?}"); + error!("{}: {e:?}", t!("failed to read upload page")); SyncError::LocalStoreError { msg: e.to_string() } })?; client.post_records(&page).await.map_err(|e| { - error!("failed to post records: {e:?}"); + error!("{}: {e:?}", t!("failed to post records")); SyncError::RemoteRequestError { msg: e.to_string() } })?; @@ -207,7 +210,7 @@ async fn sync_upload( } } - pb.finish_with_message("Uploaded records"); + pb.finish_with_message(t!("Uploaded records")); Ok(progress as i64) } @@ -227,10 +230,13 @@ async fn sync_download( let mut ret = Vec::new(); println!( - "Downloading {} records from {}/{}", - expected, - host.0.as_simple(), - tag + "{}", + t!( + "Downloading %{records} records from %{host}/%{tag}", + records=expected, + host=host.0.as_simple(), + tag=tag + ) ); let pb = ProgressBar::new(expected); @@ -261,7 +267,7 @@ async fn sync_download( } } - pb.finish_with_message("Downloaded records"); + pb.finish_with_message(t!("Downloaded records")); Ok(ret) } @@ -280,7 +286,7 @@ pub async fn sync_remote( settings.network_connect_timeout, settings.network_timeout, ) - .expect("failed to create client"); + .expect(&t!("failed to create client")); let mut uploaded = 0; let mut downloaded = Vec::new(); @@ -362,10 +368,10 @@ mod tests { ) -> (SqliteStore, Vec) { let local_store = SqliteStore::new(":memory:", test_local_timeout()) .await - .expect("failed to open in memory sqlite"); + .expect(&t!("failed to open in memory sqlite")); let remote_store = SqliteStore::new(":memory:", test_local_timeout()) .await - .expect("failed to open in memory sqlite"); // "remote" + .expect(&t!("failed to open in memory sqlite")); // "remote" for i in local_records { local_store.push(&i).await.unwrap(); diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs index a09ee45e637..b81d1bae4a0 100644 --- a/crates/atuin-client/src/settings.rs +++ b/crates/atuin-client/src/settings.rs @@ -523,13 +523,13 @@ pub struct Settings { impl Settings { pub fn utc() -> Self { Self::builder() - .expect("Could not build default") + .expect(&t!("Could not build default")) .set_override("timezone", "0") - .expect("failed to override timezone with UTC") + .expect(&t!("failed to override timezone with UTC")) .build() - .expect("Could not build config") + .expect(&t!("Could not build config")) .try_deserialize() - .expect("Could not deserialize config") + .expect(&t!("Could not deserialize config")) } fn save_to_data_dir(filename: &str, value: &str) -> Result<()> { @@ -597,14 +597,14 @@ impl Settings { if let Some(id) = id { let parsed = - Uuid::from_str(id.as_str()).expect("failed to parse host ID from local directory"); + Uuid::from_str(id.as_str()).expect(&t!("failed to parse host ID from local directory")); return Some(HostId(parsed)); } let uuid = atuin_common::utils::uuid_v7(); Settings::save_to_data_dir(HOST_ID_FILENAME, uuid.as_simple().to_string().as_ref()) - .expect("Could not write host ID to data dir"); + .expect(&t!("Could not write host ID to data dir")); Some(HostId(uuid)) } @@ -623,7 +623,7 @@ impl Settings { let d = time::Duration::try_from(d)?; Ok(OffsetDateTime::now_utc() - Settings::last_sync()? >= d) } - Err(e) => Err(eyre!("failed to check sync: {}", e)), + Err(e) => Err(eyre!("{}: {}", t!("failed to check sync"), e)), } } @@ -635,7 +635,7 @@ impl Settings { pub fn session_token(&self) -> Result { if !self.logged_in() { - return Err(eyre!("Tried to load session; not logged in")); + return Err(eyre!(t!("Tried to load session; not logged in"))); } let session_path = self.session_path.as_str(); @@ -688,7 +688,7 @@ impl Settings { Ok::<(), eyre::Report>(()) }) .await - .expect("file task panicked")?; + .expect(&t!("file task panicked"))?; Ok(latest) } @@ -820,9 +820,9 @@ impl Settings { let data_dir = atuin_common::utils::data_dir(); create_dir_all(&config_dir) - .wrap_err_with(|| format!("could not create dir {config_dir:?}"))?; + .wrap_err_with(|| t!("could not create dir %{config_dir}", config_dir=format!("{config_dir:?}")))?; - create_dir_all(&data_dir).wrap_err_with(|| format!("could not create dir {data_dir:?}"))?; + create_dir_all(&data_dir).wrap_err_with(|| t!("could not create dir %{data_dir}", data_dir=format!("{data_dir:?}")))?; let mut config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") { PathBuf::from(p) @@ -842,9 +842,9 @@ impl Settings { FileFormat::Toml, )) } else { - let mut file = File::create(config_file).wrap_err("could not create config file")?; + let mut file = File::create(config_file).wrap_err(t!("could not create config file"))?; file.write_all(EXAMPLE_CONFIG.as_bytes()) - .wrap_err("could not write default config file")?; + .wrap_err(t!("could not write default config file"))?; config_builder }; @@ -852,7 +852,7 @@ impl Settings { let config = config_builder.build()?; let mut settings: Settings = config .try_deserialize() - .map_err(|e| eyre!("failed to deserialize: {}", e))?; + .map_err(|e| eyre!("{}: {}", t!("failed to deserialize"), e))?; // all paths should be expanded let db_path = settings.db_path; @@ -880,11 +880,11 @@ impl Default for Settings { // if this panics something is very wrong, as the default config // does not build or deserialize into the settings struct Self::builder() - .expect("Could not build default") + .expect(&t!("Could not build default")) .build() - .expect("Could not build config") + .expect(&t!("Could not build config")) .try_deserialize() - .expect("Could not deserialize config") + .expect(&t!("Could not deserialize config")) } } diff --git a/crates/atuin-client/src/sync.rs b/crates/atuin-client/src/sync.rs index c2377baa42d..d8196f1dccd 100644 --- a/crates/atuin-client/src/sync.rs +++ b/crates/atuin-client/src/sync.rs @@ -39,7 +39,7 @@ async fn sync_download( client: &api_client::Client<'_>, db: &impl Database, ) -> Result<(i64, i64)> { - debug!("starting sync download"); + debug!("{}", t!("starting sync download")); let remote_status = client.status().await?; let remote_count = remote_status.count; @@ -70,8 +70,8 @@ async fn sync_download( .history .iter() // TODO: handle deletion earlier in this chain - .map(|h| serde_json::from_str(h).expect("invalid base64")) - .map(|h| decrypt(h, key).expect("failed to decrypt history! check your key")) + .map(|h| serde_json::from_str(h).expect(&t!("invalid base64"))) + .map(|h| decrypt(h, key).expect(&t!("failed to decrypt history! check your key"))) .map(|mut h| { if remote_deleted.contains(h.id.0.as_str()) { h.deleted_at = Some(time::OffsetDateTime::now_utc()); @@ -92,7 +92,7 @@ async fn sync_download( let page_last = history .last() - .expect("could not get last element of page") + .expect(&t!("could not get last element of page")) .timestamp; // in the case of a small sync frequency, it's possible for history to @@ -113,8 +113,11 @@ async fn sync_download( db.delete(h).await?; } else { info!( - "could not delete history with id {}, not found locally", - i.as_str() + "{}", + t!( + "could not delete history with id %{id}, not found locally", + id=i.as_str() + ) ); } } @@ -139,7 +142,7 @@ async fn sync_upload( let local_count = db.history_count(true).await?; - debug!("remote has {}, we have {}", remote_count, local_count); + debug!("{}", t!("remote has %{remote_count}, we have %{local_count}", remote_count=remote_count, local_count=local_count)); // first just try the most recent set let mut cursor = OffsetDateTime::now_utc(); @@ -171,7 +174,7 @@ async fn sync_upload( cursor = buffer.last().unwrap().timestamp; remote_count = client.count().await?; - debug!("upload cursor: {:?}", cursor); + debug!("{}: {:?}", t!("upload cursor"), cursor); } let deleted = db.deleted().await?; @@ -181,7 +184,7 @@ async fn sync_upload( continue; } - info!("deleting {} on remote", i.id); + info!("{}", t!("deleting %{id} on remote", id=i.id)); client.delete_history(i).await?; } @@ -204,7 +207,7 @@ pub async fn sync(settings: &Settings, force: bool, db: &impl Database) -> Resul let download = sync_download(&key, force, &client, db).await?; - debug!("sync downloaded {}", download.0); + debug!("{}", t!("sync downloaded %{num}", num=download.0)); Ok(()) } diff --git a/crates/atuin-client/src/theme.rs b/crates/atuin-client/src/theme.rs index 9ebe6f9cfe1..d235627e566 100644 --- a/crates/atuin-client/src/theme.rs +++ b/crates/atuin-client/src/theme.rs @@ -134,7 +134,8 @@ impl Theme { StyleFactory::from_fg_string(color).unwrap_or_else(|err| { if debug { log::warn!( - "Tried to load string as a color unsuccessfully: ({}={}) {}", + "{}: ({}={}) {}", + t!("Tried to load string as a color unsuccessfully"), name, color, err @@ -186,7 +187,7 @@ fn from_string(name: &str) -> Result { .filter_map(|n| n.ok()) .collect(); if vec.len() != 3 { - return Err("Could not parse 3 hex values from string".into()); + return Err(t!("Could not parse 3 hex values from string").into()); } Ok(Color::Rgb { r: vec[0], @@ -198,10 +199,10 @@ fn from_string(name: &str) -> Result { // For full flexibility, we need to use serde_json, given // crossterm's approach. serde_json::from_str::(format!("\"{}\"", &name[1..]).as_str()) - .map_err(|_| format!("Could not convert color name {} to Crossterm color", name)) + .map_err(|_| t!("Could not convert color name %{name} to Crossterm color", name=name).to_string()) } _ => { - let srgb = named::from_str(name).ok_or("No such color in palette")?; + let srgb = named::from_str(name).ok_or(t!("No such color in palette"))?; Ok(Color::Rgb { r: srgb.red, g: srgb.green, @@ -370,7 +371,7 @@ impl ThemeManager { if p.is_empty() { return Err(Box::new(Error::new( ErrorKind::NotFound, - "Empty theme directory override and could not find theme elsewhere", + t!("Empty theme directory override and could not find theme elsewhere"), ))); } PathBuf::from(p) @@ -409,11 +410,12 @@ impl ThemeManager { return Err(Box::new(Error::new( ErrorKind::InvalidInput, format!( - "Failed to deserialize theme: {}", + "{}: {:?}", + t!("Failed to deserialize theme"), if debug { e.to_string() } else { - "set theme debug on for more info".to_string() + t!("set theme debug on for more info").into() } ), ))) @@ -425,7 +427,7 @@ impl ThemeManager { if max_depth == 0 { return Err(Box::new(Error::new( ErrorKind::InvalidInput, - "Parent requested but we hit the recursion limit", + t!("Parent requested but we hit the recursion limit"), ))); } Some(self.load_theme(parent_name.as_str(), Some(max_depth - 1))) @@ -435,7 +437,8 @@ impl ThemeManager { if debug && name != theme_config.theme.name { log::warn!( - "Your theme config name is not the name of your loaded theme {} != {}", + "{} {} != {}", + t!("Your theme config name is not the name of your loaded theme"), name, theme_config.theme.name ); @@ -460,7 +463,7 @@ impl ThemeManager { None => match self.load_theme_from_file(name, max_depth.unwrap_or(DEFAULT_MAX_DEPTH)) { Ok(theme) => theme, Err(err) => { - log::warn!("Could not load theme {}: {}", name, err); + log::warn!("{}: {:?}", t!("Could not load theme %{theme}", theme=name), err); built_ins.get("default").unwrap() } }, @@ -602,6 +605,7 @@ mod theme_tests { #[test] fn test_can_use_parent_theme_for_fallbacks() { + rust_i18n::set_locale("en"); // Ensure error message in expected language. testing_logger::setup(); let mut manager = ThemeManager::new(Some(false), Some("".to_string())); @@ -706,6 +710,7 @@ mod theme_tests { #[test] fn test_can_debug_theme() { + rust_i18n::set_locale("en"); // Ensure error message in expected language. testing_logger::setup(); [true, false].iter().for_each(|debug| { let mut manager = ThemeManager::new(Some(*debug), Some("".to_string())); diff --git a/crates/atuin-common/locales/TODO.yml b/crates/atuin-common/locales/TODO.yml index 98890b0283a..04c5598f92e 100644 --- a/crates/atuin-common/locales/TODO.yml +++ b/crates/atuin-common/locales/TODO.yml @@ -4,8 +4,6 @@ $HOME not found: en: '%userprofile% not found' Atuin running with no parent!: en: Atuin running with no parent! -Failed to generate random bytes!: - en: Failed to generate random bytes! Failed to get current PID: en: Failed to get current PID Process with current pid does not exist: diff --git a/crates/atuin-common/locales/app.yml b/crates/atuin-common/locales/app.yml new file mode 100644 index 00000000000..84d036ef7e3 --- /dev/null +++ b/crates/atuin-common/locales/app.yml @@ -0,0 +1,8 @@ +_version: 2 +Failed to generate random bytes!: + en: Failed to generate random bytes! + ga: Theip ar ghiniúint beart randamach! + zh-CN: 未能生成随机字节! # FIXME: confirm with a native speaker - here as an example of non-Latin text for PR refinement +'unknown version %{version}': + en: 'unknown version %{version}' + ga: 'leagan anaithnid %{version}' diff --git a/crates/atuin-common/src/lib.rs b/crates/atuin-common/src/lib.rs index fade3891019..8990d597312 100644 --- a/crates/atuin-common/src/lib.rs +++ b/crates/atuin-common/src/lib.rs @@ -4,7 +4,7 @@ extern crate rust_i18n; pub static DEFAULT_LOCALE: &str = "en"; -i18n!("locales", fallback = "en"); +i18n!("locales"); /// Defines a new UUID type wrapper macro_rules! new_uuid { diff --git a/crates/atuin-common/src/utils.rs b/crates/atuin-common/src/utils.rs index 899982aa667..d0eaa1055ad 100644 --- a/crates/atuin-common/src/utils.rs +++ b/crates/atuin-common/src/utils.rs @@ -309,4 +309,45 @@ mod tests { assert_ne!(crypto_random_string::<16>(), crypto_random_string::<16>()); assert_ne!(crypto_random_string::<32>(), crypto_random_string::<32>()); } + + #[test] + fn ensure_locale_linked_up() { + // While there is no value in testing standard functionality of an + // external lib, this integration test checks that the locale logic + // is in place and the translations are loadable. + rust_i18n::set_locale("en"); + + assert_eq!( + t!("Failed to generate random bytes!"), + "Failed to generate random bytes!" + ); + + // Unfortunately, there is not a built-in way to use the Debug + // formatter, so (in line with recommendations for Fill in the std::fmt + // docs: https://doc.rust-lang.org/std/fmt/#fillalignment) the next best + // option is to explicitly format! it before passing. + assert_eq!( + t!("unknown version %{version}", version=format!("{:?}", (2, 1, 2))), + "unknown version (2, 1, 2)" + ); + + rust_i18n::set_locale("ga"); + + assert_eq!( + t!("Failed to generate random bytes!"), + "Theip ar ghiniúint beart randamach!" + ); + + assert_eq!( + t!("unknown version %{version}", version=format!("{:?}", (2, 1, 2))), + "leagan anaithnid (2, 1, 2)" + ); + + rust_i18n::set_locale("zx"); + + assert_eq!( + t!("Failed to generate random bytes!"), + "Failed to generate random bytes!" + ); + } } From 15f4c712b63b88340e7f63595dbc66f9cf6556cc Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 22:26:59 +0000 Subject: [PATCH 10/16] chore(i18n): fix up formatting --- crates/atuin-client/src/api_client.rs | 24 ++++++++++++++----- crates/atuin-client/src/database.rs | 13 ++++++++-- crates/atuin-client/src/history.rs | 14 ++++++++--- crates/atuin-client/src/history/store.rs | 11 +++++---- crates/atuin-client/src/import/mod.rs | 5 +++- crates/atuin-client/src/import/nu_histdb.rs | 3 ++- .../atuin-client/src/import/xonsh_sqlite.rs | 4 ++-- crates/atuin-client/src/kv.rs | 4 ++-- .../atuin-client/src/record/sqlite_store.rs | 16 +++++++++---- crates/atuin-client/src/record/sync.rs | 17 +++++++------ crates/atuin-client/src/settings.rs | 22 ++++++++++++----- crates/atuin-client/src/sync.rs | 15 ++++++++---- crates/atuin-client/src/theme.rs | 15 +++++++++--- crates/atuin-common/src/shell.rs | 14 ++++++++--- crates/atuin-common/src/utils.rs | 10 ++++++-- 15 files changed, 135 insertions(+), 52 deletions(-) diff --git a/crates/atuin-client/src/api_client.rs b/crates/atuin-client/src/api_client.rs index e99c0ea634c..9b36cf43615 100644 --- a/crates/atuin-client/src/api_client.rs +++ b/crates/atuin-client/src/api_client.rs @@ -121,7 +121,9 @@ pub fn ensure_version(response: &Response) -> Result { Err(e) => bail!("{}: {:?}", t!("failed to parse server version"), e), } } else { - bail!(t!("Server not reporting its version: it is either too old or unhealthy")); + bail!(t!( + "Server not reporting its version: it is either too old or unhealthy" + )); }?; // If the client is newer than the server @@ -140,9 +142,9 @@ async fn handle_resp_error(resp: Response) -> Result { let status = resp.status(); if status == StatusCode::SERVICE_UNAVAILABLE { - bail!( - t!("Service unavailable: check https://status.atuin.sh (or get in touch with your host)") - ); + bail!(t!( + "Service unavailable: check https://status.atuin.sh (or get in touch with your host)" + )); } if status == StatusCode::TOO_MANY_REQUESTS { @@ -154,7 +156,10 @@ async fn handle_resp_error(resp: Response) -> Result { let reason = error.reason; if status.is_client_error() { - bail!("{}: {status} - {reason}.", t!("Invalid request to the service")) + bail!( + "{}: {status} - {reason}.", + t!("Invalid request to the service") + ) } bail!(t!("There was an error with the atuin sync service, server error %{status}: %{reason}.\nIf the problem persists, contact the host", status=status, reason=reason)) @@ -304,7 +309,14 @@ impl<'a> Client<'a> { let url = format!("{}/api/v0/record", self.sync_addr); let url = Url::parse(url.as_str())?; - debug!("{}", t!("uploading %{records} records to %{url}", records=records.len(), url=url)); + debug!( + "{}", + t!( + "uploading %{records} records to %{url}", + records = records.len(), + url = url + ) + ); let resp = self.client.post(url).json(records).send().await?; handle_resp_error(resp).await?; diff --git a/crates/atuin-client/src/database.rs b/crates/atuin-client/src/database.rs index 7ec98bb442e..48a61ad8c60 100644 --- a/crates/atuin-client/src/database.rs +++ b/crates/atuin-client/src/database.rs @@ -335,7 +335,14 @@ impl Database for Sqlite { } async fn range(&self, from: OffsetDateTime, to: OffsetDateTime) -> Result> { - debug!("{}", t!("listing history from %{from} to %{to}", from=format!("{from:?}"), to=format!("{to:?}"))); + debug!( + "{}", + t!( + "listing history from %{from} to %{to}", + from = format!("{from:?}"), + to = format!("{to:?}") + ) + ); let res = sqlx::query( "select * from history where timestamp >= ?1 and timestamp <= ?2 order by timestamp asc", @@ -699,7 +706,9 @@ impl Database for Sqlite { let total = total.sql().expect(&t!("issue in stats average query")); let average = average.sql().expect(&t!("issue in stats previous query")); let exits = exits.sql().expect(&t!("issue in stats exits query")); - let day_of_week = day_of_week.sql().expect(&t!("issue in stats day of week query")); + let day_of_week = day_of_week + .sql() + .expect(&t!("issue in stats day of week query")); let duration_over_time = duration_over_time .sql() .expect(&t!("issue in stats duration over time query")); diff --git a/crates/atuin-client/src/history.rs b/crates/atuin-client/src/history.rs index 6738f557935..5bc4404f462 100644 --- a/crates/atuin-client/src/history.rs +++ b/crates/atuin-client/src/history.rs @@ -164,13 +164,18 @@ impl History { let version = decode::read_u16(&mut bytes).map_err(error_report)?; if version != 0 { - bail!(t!("expected decoding v0 record, found v%{version}", version=version)); + bail!(t!( + "expected decoding v0 record, found v%{version}", + version = version + )); } let nfields = decode::read_array_len(&mut bytes).map_err(error_report)?; if nfields != 9 { - bail!(t!("cannot decrypt history from a different version of Atuin")); + bail!(t!( + "cannot decrypt history from a different version of Atuin" + )); } let bytes = bytes.remaining_slice(); @@ -220,7 +225,10 @@ impl History { match version { HISTORY_VERSION => Self::deserialize_v0(bytes), - _ => bail!(t!("unknown version %{version}", version=format!("{:?}", version))), + _ => bail!(t!( + "unknown version %{version}", + version = format!("{:?}", version) + )), } } diff --git a/crates/atuin-client/src/history/store.rs b/crates/atuin-client/src/history/store.rs index b29e4add240..9d1aeb24efd 100644 --- a/crates/atuin-client/src/history/store.rs +++ b/crates/atuin-client/src/history/store.rs @@ -95,7 +95,7 @@ impl HistoryRecord { if !bytes.is_empty() { bail!(t!( "trailing bytes decoding HistoryRecord::Delete - malformed? got %{bytes}", - bytes=format!("{bytes:?}") + bytes = format!("{bytes:?}") )); } @@ -103,7 +103,7 @@ impl HistoryRecord { } n => { - bail!(t!("unknown HistoryRecord type %{n}", n=n)) + bail!(t!("unknown HistoryRecord type %{n}", n = n)) } } } @@ -202,7 +202,10 @@ impl HistoryStore { HistoryRecord::deserialize(&decrypted.data, HISTORY_VERSION) } - version => bail!(t!("unknown history version %{version}", version=format!("{version:?}"))), + version => bail!(t!( + "unknown history version %{version}", + version = format!("{version:?}") + )), }?; ret.push(hist); @@ -315,7 +318,7 @@ impl HistoryStore { debug!("loaded {}", i.id); if store_ids.contains(&i.id) { - debug!("{}", t!("skipping {id} - already exists", id=i.id)); + debug!("{}", t!("skipping {id} - already exists", id = i.id)); continue; } diff --git a/crates/atuin-client/src/import/mod.rs b/crates/atuin-client/src/import/mod.rs index bef01c7ad10..d28936189ee 100644 --- a/crates/atuin-client/src/import/mod.rs +++ b/crates/atuin-client/src/import/mod.rs @@ -89,7 +89,10 @@ fn is_file(p: PathBuf) -> Result { if p.is_file() { Ok(p) } else { - bail!(t!("Could not find history file %{histfile}. Try setting $HISTFILE", histfile=format!("{p:?}"))) + bail!(t!( + "Could not find history file %{histfile}. Try setting $HISTFILE", + histfile = format!("{p:?}") + )) } } diff --git a/crates/atuin-client/src/import/nu_histdb.rs b/crates/atuin-client/src/import/nu_histdb.rs index 15f92a889f8..67e51233de8 100644 --- a/crates/atuin-client/src/import/nu_histdb.rs +++ b/crates/atuin-client/src/import/nu_histdb.rs @@ -73,7 +73,8 @@ async fn hist_from_db_conn(pool: Pool) -> Result> impl NuHistDb { pub fn histpath() -> Result { - let base = BaseDirs::new().ok_or_else(|| eyre!(t!("could not determine data directory")))?; + let base = + BaseDirs::new().ok_or_else(|| eyre!(t!("could not determine data directory")))?; let config_dir = base.config_dir().join("nushell"); let histdb_path = config_dir.join("history.sqlite3"); diff --git a/crates/atuin-client/src/import/xonsh_sqlite.rs b/crates/atuin-client/src/import/xonsh_sqlite.rs index e6333d226c0..cedd4bd4aa0 100644 --- a/crates/atuin-client/src/import/xonsh_sqlite.rs +++ b/crates/atuin-client/src/import/xonsh_sqlite.rs @@ -75,7 +75,7 @@ fn xonsh_db_path(xonsh_data_dir: Option) -> Result { } else { Err(eyre!(t!( "Could not find xonsh history db at: %{hist_file}", - hist_file=hist_file.to_string_lossy() + hist_file = hist_file.to_string_lossy() ))) } } @@ -131,7 +131,7 @@ impl Importer for XonshSqlite { count += 1; } - println!("{}", t!("Loaded: %{count}", count=count)); + println!("{}", t!("Loaded: %{count}", count = count)); Ok(()) } } diff --git a/crates/atuin-client/src/kv.rs b/crates/atuin-client/src/kv.rs index fb5bc6ff554..5dc2dcd3d38 100644 --- a/crates/atuin-client/src/kv.rs +++ b/crates/atuin-client/src/kv.rs @@ -99,7 +99,7 @@ impl KvStore { if value.len() > KV_VAL_MAX_LEN { return Err(eyre!(t!( "kv value too large: max len {len} bytes", - len=KV_VAL_MAX_LEN + len = KV_VAL_MAX_LEN ))); } @@ -176,7 +176,7 @@ impl KvStore { for record in tagged { let decrypted = match record.version.as_str() { KV_VERSION => record.decrypt::(encryption_key)?, - version => bail!("{} {version:?}", t!("unknown version"), version=version), + version => bail!("{} {version:?}", t!("unknown version"), version = version), }; let kv = KvRecord::deserialize(&decrypted.data, KV_VERSION)?; diff --git a/crates/atuin-client/src/record/sqlite_store.rs b/crates/atuin-client/src/record/sqlite_store.rs index e3a8fec1d2e..24934c7dfbe 100644 --- a/crates/atuin-client/src/record/sqlite_store.rs +++ b/crates/atuin-client/src/record/sqlite_store.rs @@ -31,7 +31,13 @@ impl SqliteStore { pub async fn new(path: impl AsRef, timeout: f64) -> Result { let path = path.as_ref(); - debug!("{}", t!("opening sqlite database at %{path}", path=format!("{path:?}"))); + debug!( + "{}", + t!( + "opening sqlite database at %{path}", + path = format!("{path:?}") + ) + ); let create = !path.exists(); if create { @@ -92,7 +98,8 @@ impl SqliteStore { // tbh at this point things are pretty fucked so just panic let id = Uuid::from_str(row.get("id")).expect(&t!("invalid id UUID format in sqlite DB")); - let host = Uuid::from_str(row.get("host")).expect(&t!("invalid host UUID format in sqlite DB")); + let host = + Uuid::from_str(row.get("host")).expect(&t!("invalid host UUID format in sqlite DB")); Record { id: RecordId(id), @@ -269,7 +276,8 @@ impl Store for SqliteStore { for i in res { let host = HostId( - Uuid::from_str(i.0.as_str()).expect(&t!("failed to parse uuid for local store status")), + Uuid::from_str(i.0.as_str()) + .expect(&t!("failed to parse uuid for local store status")), ); status.set_raw(host, i.1, i.2 as u64); @@ -352,7 +360,7 @@ impl Store for SqliteStore { "{}", t!( "Failed to decrypt %{record}, deleting", - record=record.id.0.as_hyphenated() + record = record.id.0.as_hyphenated() ) ); diff --git a/crates/atuin-client/src/record/sync.rs b/crates/atuin-client/src/record/sync.rs index 62b2b0bfb00..f8b601d0e38 100644 --- a/crates/atuin-client/src/record/sync.rs +++ b/crates/atuin-client/src/record/sync.rs @@ -130,9 +130,8 @@ pub async fn operations( // something is pretty fucked. (None, None) => { return Err(SyncError::SyncLogicError { - msg: t!( - "diff has nothing for local or remote - (host, tag) does not exist", - ).to_string(), + msg: t!("diff has nothing for local or remote - (host, tag) does not exist",) + .to_string(), }) } }; @@ -179,9 +178,9 @@ async fn sync_upload( "{}", t!( "Uploading %{records} records to %{host}/%{tag}", - records=expected, - host=host.0.as_simple(), - tag=tag + records = expected, + host = host.0.as_simple(), + tag = tag ) ); @@ -233,9 +232,9 @@ async fn sync_download( "{}", t!( "Downloading %{records} records from %{host}/%{tag}", - records=expected, - host=host.0.as_simple(), - tag=tag + records = expected, + host = host.0.as_simple(), + tag = tag ) ); diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs index b81d1bae4a0..7a5fe4d038c 100644 --- a/crates/atuin-client/src/settings.rs +++ b/crates/atuin-client/src/settings.rs @@ -596,8 +596,8 @@ impl Settings { let id = Settings::read_from_data_dir(HOST_ID_FILENAME); if let Some(id) = id { - let parsed = - Uuid::from_str(id.as_str()).expect(&t!("failed to parse host ID from local directory")); + let parsed = Uuid::from_str(id.as_str()) + .expect(&t!("failed to parse host ID from local directory")); return Some(HostId(parsed)); } @@ -819,10 +819,19 @@ impl Settings { let config_dir = atuin_common::utils::config_dir(); let data_dir = atuin_common::utils::data_dir(); - create_dir_all(&config_dir) - .wrap_err_with(|| t!("could not create dir %{config_dir}", config_dir=format!("{config_dir:?}")))?; + create_dir_all(&config_dir).wrap_err_with(|| { + t!( + "could not create dir %{config_dir}", + config_dir = format!("{config_dir:?}") + ) + })?; - create_dir_all(&data_dir).wrap_err_with(|| t!("could not create dir %{data_dir}", data_dir=format!("{data_dir:?}")))?; + create_dir_all(&data_dir).wrap_err_with(|| { + t!( + "could not create dir %{data_dir}", + data_dir = format!("{data_dir:?}") + ) + })?; let mut config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") { PathBuf::from(p) @@ -842,7 +851,8 @@ impl Settings { FileFormat::Toml, )) } else { - let mut file = File::create(config_file).wrap_err(t!("could not create config file"))?; + let mut file = + File::create(config_file).wrap_err(t!("could not create config file"))?; file.write_all(EXAMPLE_CONFIG.as_bytes()) .wrap_err(t!("could not write default config file"))?; diff --git a/crates/atuin-client/src/sync.rs b/crates/atuin-client/src/sync.rs index d8196f1dccd..1215b4ec51f 100644 --- a/crates/atuin-client/src/sync.rs +++ b/crates/atuin-client/src/sync.rs @@ -116,7 +116,7 @@ async fn sync_download( "{}", t!( "could not delete history with id %{id}, not found locally", - id=i.as_str() + id = i.as_str() ) ); } @@ -142,7 +142,14 @@ async fn sync_upload( let local_count = db.history_count(true).await?; - debug!("{}", t!("remote has %{remote_count}, we have %{local_count}", remote_count=remote_count, local_count=local_count)); + debug!( + "{}", + t!( + "remote has %{remote_count}, we have %{local_count}", + remote_count = remote_count, + local_count = local_count + ) + ); // first just try the most recent set let mut cursor = OffsetDateTime::now_utc(); @@ -184,7 +191,7 @@ async fn sync_upload( continue; } - info!("{}", t!("deleting %{id} on remote", id=i.id)); + info!("{}", t!("deleting %{id} on remote", id = i.id)); client.delete_history(i).await?; } @@ -207,7 +214,7 @@ pub async fn sync(settings: &Settings, force: bool, db: &impl Database) -> Resul let download = sync_download(&key, force, &client, db).await?; - debug!("{}", t!("sync downloaded %{num}", num=download.0)); + debug!("{}", t!("sync downloaded %{num}", num = download.0)); Ok(()) } diff --git a/crates/atuin-client/src/theme.rs b/crates/atuin-client/src/theme.rs index d235627e566..b6d7c015e05 100644 --- a/crates/atuin-client/src/theme.rs +++ b/crates/atuin-client/src/theme.rs @@ -198,8 +198,13 @@ fn from_string(name: &str) -> Result { '@' => { // For full flexibility, we need to use serde_json, given // crossterm's approach. - serde_json::from_str::(format!("\"{}\"", &name[1..]).as_str()) - .map_err(|_| t!("Could not convert color name %{name} to Crossterm color", name=name).to_string()) + serde_json::from_str::(format!("\"{}\"", &name[1..]).as_str()).map_err(|_| { + t!( + "Could not convert color name %{name} to Crossterm color", + name = name + ) + .to_string() + }) } _ => { let srgb = named::from_str(name).ok_or(t!("No such color in palette"))?; @@ -463,7 +468,11 @@ impl ThemeManager { None => match self.load_theme_from_file(name, max_depth.unwrap_or(DEFAULT_MAX_DEPTH)) { Ok(theme) => theme, Err(err) => { - log::warn!("{}: {:?}", t!("Could not load theme %{theme}", theme=name), err); + log::warn!( + "{}: {:?}", + t!("Could not load theme %{theme}", theme = name), + err + ); built_ins.get("default").unwrap() } }, diff --git a/crates/atuin-common/src/shell.rs b/crates/atuin-common/src/shell.rs index 7a841db36a2..d444ccda230 100644 --- a/crates/atuin-common/src/shell.rs +++ b/crates/atuin-common/src/shell.rs @@ -53,7 +53,11 @@ impl Shell { .expect(&t!("Process with current pid does not exist")); let parent = sys - .process(process.parent().expect(&t!("Atuin running with no parent!"))) + .process( + process + .parent() + .expect(&t!("Atuin running with no parent!")), + ) .expect(&t!("Process with parent pid does not exist")); let shell = parent.name().trim().to_lowercase(); @@ -166,8 +170,12 @@ pub fn shell_name(parent: Option<&Process>) -> String { .process(get_current_pid().expect(&t!("Failed to get current PID"))) .expect(&t!("Process with current pid does not exist")); - sys.process(process.parent().expect(&t!("Atuin running with no parent!"))) - .expect(&t!("Process with parent pid does not exist")) + sys.process( + process + .parent() + .expect(&t!("Atuin running with no parent!")), + ) + .expect(&t!("Process with parent pid does not exist")) }; let shell = parent.name().trim().to_lowercase(); diff --git a/crates/atuin-common/src/utils.rs b/crates/atuin-common/src/utils.rs index d0eaa1055ad..047ba9858d9 100644 --- a/crates/atuin-common/src/utils.rs +++ b/crates/atuin-common/src/utils.rs @@ -327,7 +327,10 @@ mod tests { // docs: https://doc.rust-lang.org/std/fmt/#fillalignment) the next best // option is to explicitly format! it before passing. assert_eq!( - t!("unknown version %{version}", version=format!("{:?}", (2, 1, 2))), + t!( + "unknown version %{version}", + version = format!("{:?}", (2, 1, 2)) + ), "unknown version (2, 1, 2)" ); @@ -339,7 +342,10 @@ mod tests { ); assert_eq!( - t!("unknown version %{version}", version=format!("{:?}", (2, 1, 2))), + t!( + "unknown version %{version}", + version = format!("{:?}", (2, 1, 2)) + ), "leagan anaithnid (2, 1, 2)" ); From 5a9b1e5404257a6fdf4b1a0b6e000febc44cc862 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 22:38:40 +0000 Subject: [PATCH 11/16] fix(i18n): missing translation for inspector --- crates/atuin/locales/TODO.yml | 2 ++ crates/atuin/src/command/client/search/interactive.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/atuin/locales/TODO.yml b/crates/atuin/locales/TODO.yml index b728b8f5ed2..9a84c979863 100644 --- a/crates/atuin/locales/TODO.yml +++ b/crates/atuin/locales/TODO.yml @@ -1,4 +1,6 @@ _version: 2 could not load client settings: en: could not load client settings +Drawing inspector, but no stats: + en: Drawing inspector, but no stats diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs index 4672c1ab51d..76d3128b310 100644 --- a/crates/atuin/src/command/client/search/interactive.rs +++ b/crates/atuin/src/command/client/search/interactive.rs @@ -740,7 +740,7 @@ impl State { f, results_list_chunk, &results[self.results_state.selected()], - &stats.expect("Drawing inspector, but no stats"), + &stats.expect(&t!("Drawing inspector, but no stats")), theme, ); } From df7c1f827f03af2cc99b84218e93b7784ece65f9 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 22:56:51 +0000 Subject: [PATCH 12/16] fix(i18n): remove debug formatter from translation string --- crates/atuin-client/locales/TODO.yml | 4 ++-- crates/atuin-client/src/api_client.rs | 2 +- crates/atuin-client/src/kv.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/atuin-client/locales/TODO.yml b/crates/atuin-client/locales/TODO.yml index 85f88b18cac..70eca35afd6 100644 --- a/crates/atuin-client/locales/TODO.yml +++ b/crates/atuin-client/locales/TODO.yml @@ -79,8 +79,8 @@ Server: en: 'Service unavailable: check https://status.atuin.sh (or get in touch with your host)' 'There was an error with the atuin sync service, server error %{status}: %{reason}. If the problem persists, contact the host': en: 'There was an error with the atuin sync service, server error %{status}: %{reason}. If the problem persists, contact the host' -'There was an error with the atuin sync service: Status %{status:?}. If the problem persists, contact the host': - en: 'There was an error with the atuin sync service: Status %{status:?}. If the problem persists, contact the host' +'There was an error with the atuin sync service: Status %{status}. If the problem persists, contact the host': + en: 'There was an error with the atuin sync service: Status %{status}. If the problem persists, contact the host' Time reversal detected in Bash history! Commands may be ordered incorrectly.: en: 'Time reversal detected in Bash history! Commands may be ordered incorrectly.' Tried to load session; not logged in: diff --git a/crates/atuin-client/src/api_client.rs b/crates/atuin-client/src/api_client.rs index 9b36cf43615..233263cd53b 100644 --- a/crates/atuin-client/src/api_client.rs +++ b/crates/atuin-client/src/api_client.rs @@ -165,7 +165,7 @@ async fn handle_resp_error(resp: Response) -> Result { bail!(t!("There was an error with the atuin sync service, server error %{status}: %{reason}.\nIf the problem persists, contact the host", status=status, reason=reason)) } - bail!(t!("There was an error with the atuin sync service: Status %{status:?}.\nIf the problem persists, contact the host", status=status)) + bail!(t!("There was an error with the atuin sync service: Status %{status}.\nIf the problem persists, contact the host", status=format!("{status:?}"))) } Ok(resp) diff --git a/crates/atuin-client/src/kv.rs b/crates/atuin-client/src/kv.rs index 5dc2dcd3d38..5b91e5d2242 100644 --- a/crates/atuin-client/src/kv.rs +++ b/crates/atuin-client/src/kv.rs @@ -66,7 +66,7 @@ impl KvRecord { }) } _ => { - bail!(t!("unknown version %{version:?}")) + bail!(t!("unknown version %{version}", version=format!("{version:?}"))) } } } From 0d618389c88da666f2a854da75606831ffc4cd55 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 23:21:29 +0000 Subject: [PATCH 13/16] fix(i18n): remove fallback locale to force using the DEFAULT_LOCALE from atuin-common --- crates/atuin-client/src/lib.rs | 2 +- crates/atuin/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/atuin-client/src/lib.rs b/crates/atuin-client/src/lib.rs index 024fa1d6246..f41624e55e9 100644 --- a/crates/atuin-client/src/lib.rs +++ b/crates/atuin-client/src/lib.rs @@ -27,4 +27,4 @@ pub mod theme; mod utils; -i18n!("locales", fallback = "en"); +i18n!("locales"); diff --git a/crates/atuin/src/main.rs b/crates/atuin/src/main.rs index 98f3c2634d2..2bff7e96176 100644 --- a/crates/atuin/src/main.rs +++ b/crates/atuin/src/main.rs @@ -26,7 +26,7 @@ static HELP_TEMPLATE: &str = "\ {all-args}{after-help}"; -i18n!("locales", fallback = "en"); +i18n!("locales"); /// Magical shell history #[derive(Parser)] From 4bfde9d5f183879396b54dd636b7ff0c1abc75d0 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 23:23:21 +0000 Subject: [PATCH 14/16] fix(i18n): ensure Cargo.lock up-to-date for i18n --- Cargo.lock | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 278 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc311e7dae6..86e2a0c7239 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,12 +242,13 @@ dependencies = [ "fuzzy-matcher", "indicatif", "interim", - "itertools", + "itertools 0.13.0", "log", "ratatui", "regex", "rpassword", "runtime-format", + "rust-i18n", "rustix", "semver", "serde", @@ -283,7 +284,7 @@ dependencies = [ "humantime", "indicatif", "interim", - "itertools", + "itertools 0.13.0", "lazy_static", "log", "memchr", @@ -294,6 +295,7 @@ dependencies = [ "regex", "reqwest 0.11.27", "rmp", + "rust-i18n", "rusty_paserk", "rusty_paseto", "semver", @@ -328,9 +330,11 @@ dependencies = [ "getrandom", "lazy_static", "pretty_assertions", + "rust-i18n", "semver", "serde", "sqlx", + "sys-locale", "sysinfo", "thiserror 1.0.69", "time", @@ -551,6 +555,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base62" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10e52a7bcb1d6beebee21fb5053af9e3cbb7a7ed1a4909e534040e676437ab1f" +dependencies = [ + "rustversion", +] + [[package]] name = "base64" version = "0.21.7" @@ -617,6 +630,16 @@ dependencies = [ "objc2", ] +[[package]] +name = "bstr" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -855,7 +878,7 @@ dependencies = [ "nom", "pathdiff", "serde", - "toml", + "toml 0.5.11", ] [[package]] @@ -1623,6 +1646,36 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "h2" version = "0.3.26" @@ -2102,6 +2155,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.9", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.25.5" @@ -2216,6 +2285,15 @@ dependencies = [ "nom", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -2299,6 +2377,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libyml" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64804cc6a5042d4f05379909ba25b503ec04e2c082151d62122d5dcaa274b961" + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2566,6 +2650,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "normpath" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -2970,7 +3063,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] @@ -3140,7 +3233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" dependencies = [ "heck", - "itertools", + "itertools 0.13.0", "log", "multimap", "once_cell", @@ -3160,7 +3253,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ "anyhow", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", "syn", @@ -3343,7 +3436,7 @@ dependencies = [ "crossterm", "indoc", "instability", - "itertools", + "itertools 0.13.0", "lru", "paste", "strum", @@ -3611,6 +3704,60 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "rust-i18n" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039f57d22229db401af3458ca939300178e99e88b938573cea12b7c2b0f09724" +dependencies = [ + "globwalk", + "once_cell", + "regex", + "rust-i18n-macro", + "rust-i18n-support", + "smallvec", +] + +[[package]] +name = "rust-i18n-macro" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde5c022360a2e54477882843d56b6f9bcb4bc62f504b651a2f497f0028d174f" +dependencies = [ + "glob", + "once_cell", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "serde_yml", + "syn", +] + +[[package]] +name = "rust-i18n-support" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75d2844d36f62b5d6b66f9cf8f8cbdbbbdcdb5fd37a473a9cc2fb45fdcf485d2" +dependencies = [ + "arc-swap", + "base62", + "globwalk", + "itertools 0.11.0", + "lazy_static", + "normpath", + "once_cell", + "proc-macro2", + "regex", + "serde", + "serde_json", + "serde_yml", + "siphasher 1.0.1", + "toml 0.7.8", + "triomphe", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -3801,6 +3948,15 @@ dependencies = [ "cipher", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.27" @@ -3913,6 +4069,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3955,6 +4120,23 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_yml" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c" +dependencies = [ + "indexmap 2.7.0", + "itoa", + "libyml", + "log", + "memchr", + "ryu", + "serde", + "serde_json", + "tempfile", +] + [[package]] name = "sha1" version = "0.10.6" @@ -4053,6 +4235,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "sketches-ddsketch" version = "0.2.2" @@ -4426,6 +4614,15 @@ dependencies = [ "syn", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "sysinfo" version = "0.30.13" @@ -4714,6 +4911,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.7.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.12.3" @@ -4921,6 +5152,17 @@ dependencies = [ "petgraph", ] +[[package]] +name = "triomphe" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +dependencies = [ + "arc-swap", + "serde", + "stable_deref_trait", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -4992,7 +5234,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools", + "itertools 0.13.0", "unicode-segmentation", "unicode-width 0.1.14", ] @@ -5094,6 +5336,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -5317,6 +5569,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -5520,6 +5781,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" From 25c9d49276e2b4ae70f4fbcb7dd35cf7e6b06c3f Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 23:30:27 +0000 Subject: [PATCH 15/16] chore: fix error format, clippy and rust format --- crates/atuin-client/src/kv.rs | 5 ++++- crates/atuin-client/src/theme.rs | 2 +- .../src/command/client/search/interactive.rs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/atuin-client/src/kv.rs b/crates/atuin-client/src/kv.rs index 5b91e5d2242..54998328a6b 100644 --- a/crates/atuin-client/src/kv.rs +++ b/crates/atuin-client/src/kv.rs @@ -66,7 +66,10 @@ impl KvRecord { }) } _ => { - bail!(t!("unknown version %{version}", version=format!("{version:?}"))) + bail!(t!( + "unknown version %{version}", + version = format!("{version:?}") + )) } } } diff --git a/crates/atuin-client/src/theme.rs b/crates/atuin-client/src/theme.rs index b6d7c015e05..f861a6f0b00 100644 --- a/crates/atuin-client/src/theme.rs +++ b/crates/atuin-client/src/theme.rs @@ -469,7 +469,7 @@ impl ThemeManager { Ok(theme) => theme, Err(err) => { log::warn!( - "{}: {:?}", + "{}: {}", t!("Could not load theme %{theme}", theme = name), err ); diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs index 76d3128b310..9dd0b61f021 100644 --- a/crates/atuin/src/command/client/search/interactive.rs +++ b/crates/atuin/src/command/client/search/interactive.rs @@ -605,13 +605,13 @@ impl State { border_size, preview_width, ); - let show_help = settings.show_help && (!compact || f.area().height > 1); // This is an OR, as it seems more likely for someone to wish to override // tabs unexpectedly being missed, than unexpectedly present. let hide_extra = settings.auto_hide_height != 0 && compact && f.area().height <= settings.auto_hide_height; - let show_tabs = settings.show_tabs && !hide_extra; + let tab_col = if settings.show_tabs && !hide_extra { 1 } else { 0 }; + let help_col = if settings.show_help && (!compact || f.area().height > 1) { 1 } else { 0 }; let chunks = Layout::default() .direction(Direction::Vertical) .margin(0) @@ -622,12 +622,12 @@ impl State { Constraint::Length(1 + border_size), // input Constraint::Min(1), // results list Constraint::Length(preview_height), // preview - Constraint::Length(if show_tabs { 1 } else { 0 }), // tabs - Constraint::Length(if show_help { 1 } else { 0 }), // header (sic) + Constraint::Length(tab_col), // tabs + Constraint::Length(help_col), // header (sic) ] } else if hide_extra { [ - Constraint::Length(if show_help { 1 } else { 0 }), // header + Constraint::Length(help_col), // header Constraint::Length(0), // tabs Constraint::Min(1), // results list Constraint::Length(0), @@ -635,8 +635,8 @@ impl State { ] } else { [ - Constraint::Length(if show_help { 1 } else { 0 }), // header - Constraint::Length(if show_tabs { 1 } else { 0 }), // tabs + Constraint::Length(help_col), // header + Constraint::Length(tab_col), // tabs Constraint::Min(1), // results list Constraint::Length(1 + border_size), // input Constraint::Length(preview_height), // preview @@ -662,7 +662,7 @@ impl State { .map(Line::from) .collect(); - if show_tabs { + if tab_col == 1 { let tabs = Tabs::new(titles) .block(Block::default().borders(Borders::NONE)) .select(self.tab_index) From 31fa758a67bf12170d6b00ef5caf61e7888163a5 Mon Sep 17 00:00:00 2001 From: Phil Weir Date: Sun, 19 Jan 2025 23:34:39 +0000 Subject: [PATCH 16/16] chore: minor format - maybe nicer way of doing it but this function is at the limit of allowed complexity --- .../src/command/client/search/interactive.rs | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs index 9dd0b61f021..db90b32ae66 100644 --- a/crates/atuin/src/command/client/search/interactive.rs +++ b/crates/atuin/src/command/client/search/interactive.rs @@ -610,8 +610,16 @@ impl State { let hide_extra = settings.auto_hide_height != 0 && compact && f.area().height <= settings.auto_hide_height; - let tab_col = if settings.show_tabs && !hide_extra { 1 } else { 0 }; - let help_col = if settings.show_help && (!compact || f.area().height > 1) { 1 } else { 0 }; + let tab_col = if settings.show_tabs && !hide_extra { + 1 + } else { + 0 + }; + let help_col = if settings.show_help && (!compact || f.area().height > 1) { + 1 + } else { + 0 + }; let chunks = Layout::default() .direction(Direction::Vertical) .margin(0) @@ -619,27 +627,27 @@ impl State { .constraints::<&[Constraint]>( if invert { [ - Constraint::Length(1 + border_size), // input - Constraint::Min(1), // results list - Constraint::Length(preview_height), // preview - Constraint::Length(tab_col), // tabs - Constraint::Length(help_col), // header (sic) + Constraint::Length(1 + border_size), // input + Constraint::Min(1), // results list + Constraint::Length(preview_height), // preview + Constraint::Length(tab_col), // tabs + Constraint::Length(help_col), // header (sic) ] } else if hide_extra { [ Constraint::Length(help_col), // header - Constraint::Length(0), // tabs - Constraint::Min(1), // results list + Constraint::Length(0), // tabs + Constraint::Min(1), // results list Constraint::Length(0), Constraint::Length(0), ] } else { [ - Constraint::Length(help_col), // header - Constraint::Length(tab_col), // tabs - Constraint::Min(1), // results list - Constraint::Length(1 + border_size), // input - Constraint::Length(preview_height), // preview + Constraint::Length(help_col), // header + Constraint::Length(tab_col), // tabs + Constraint::Min(1), // results list + Constraint::Length(1 + border_size), // input + Constraint::Length(preview_height), // preview ] } .as_ref(),