Skip to content

Commit

Permalink
feat: i18n (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
fujiapple852 committed Aug 27, 2024
1 parent bb2b40a commit 8845ab3
Show file tree
Hide file tree
Showing 16 changed files with 492 additions and 81 deletions.
306 changes: 290 additions & 16 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ petgraph = "0.6.5"
pretty_assertions = "1.4.0"
rand = "0.8.5"
ratatui = "0.28.1"
rust-i18n = "3.1.2"
serde = { version = "1.0.201", default-features = false }
serde_json = { version = "1.0.117", default-features = false }
serde_with = "3.9.0"
Expand Down
9 changes: 0 additions & 9 deletions crates/trippy-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,6 @@ impl PrivilegeMode {
}
}

impl Display for PrivilegeMode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Privileged => write!(f, "privileged"),
Self::Unprivileged => write!(f, "unprivileged"),
}
}
}

/// The ICMP extension parsing mode.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum IcmpExtensionParseMode {
Expand Down
1 change: 1 addition & 0 deletions crates/trippy-tui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ itertools.workspace = true
maxminddb.workspace = true
petgraph.workspace = true
ratatui.workspace = true
rust-i18n.workspace = true
serde = { workspace = true, default-features = false, features = [ "derive" ] }
serde_json.workspace = true
serde_with.workspace = true
Expand Down
3 changes: 2 additions & 1 deletion crates/trippy-tui/src/frontend/render/bsod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, BorderType, Borders, Paragraph};
use ratatui::Frame;
use rust_i18n::t;

/// Render a blue screen of death.
pub fn render(f: &mut Frame<'_>, rect: Rect, error: &str) {
let chunks = Layout::default()
.constraints([Constraint::Percentage(35), Constraint::Percentage(65)].as_ref())
.split(rect);
let block = Block::default()
.title("Hops")
.title(t!("title_hops").to_string())
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.style(Style::default().bg(Color::Blue));
Expand Down
3 changes: 2 additions & 1 deletion crates/trippy-tui/src/frontend/render/flows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ratatui::style::{Modifier, Style};
use ratatui::text::Line;
use ratatui::widgets::{Bar, BarChart, BarGroup, Block, BorderType, Borders};
use ratatui::Frame;
use rust_i18n::t;

/// Render the flows.
pub fn render(f: &mut Frame<'_>, rect: Rect, app: &TuiApp) {
Expand Down Expand Up @@ -35,7 +36,7 @@ pub fn render(f: &mut Frame<'_>, rect: Rect, app: &TuiApp) {
})
.collect();
let block = Block::default()
.title("Flows")
.title(t!("title_flows").to_string())
.title_alignment(Alignment::Left)
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
Expand Down
107 changes: 68 additions & 39 deletions crates/trippy-tui/src/frontend/render/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, BorderType, Borders, Paragraph};
use ratatui::Frame;
use rust_i18n::t;
use std::borrow::Cow;
use std::net::IpAddr;
use std::time::Duration;
use trippy_core::{Hop, PortDirection, Protocol};
use trippy_core::{Hop, PortDirection, PrivilegeMode, Protocol};
use trippy_dns::{ResolveMethod, Resolver};

/// Render the title, config, target, clock and keyboard controls.
#[allow(clippy::too_many_lines)]
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
let header_block = Block::default()
.title(format!(" Trippy v{} ", clap::crate_version!()))
.title(format!(" {} v{} ", t!("trippy"), clap::crate_version!()))
.title_alignment(Alignment::Center)
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
Expand All @@ -27,6 +29,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
);
let now = chrono::Local::now().to_rfc3339_opts(SecondsFormat::Secs, true);
let clock_span = Line::from(Span::raw(now));
// TODO
let help_span = Line::from(vec![
Span::styled("h", Style::default().add_modifier(Modifier::BOLD)),
Span::raw("elp "),
Expand All @@ -42,80 +45,98 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
.alignment(Alignment::Right);
let protocol = match app.tracer_config().data.protocol() {
Protocol::Icmp => format!(
"icmp({}, {})",
render_target_family(app.tracer_config().data.target_addr()),
app.tracer_config().data.privilege_mode()
"{}({}, {})",
t!("icmp"),
fmt_target_family(app.tracer_config().data.target_addr()),
fmt_privilege_mode(app.tracer_config().data.privilege_mode())
),
Protocol::Udp => format!(
"udp({}, {}, {})",
render_target_family(app.tracer_config().data.target_addr()),
"{}({}, {}, {})",
t!("udp"),
fmt_target_family(app.tracer_config().data.target_addr()),
app.tracer_config().data.multipath_strategy(),
app.tracer_config().data.privilege_mode()
fmt_privilege_mode(app.tracer_config().data.privilege_mode())
),
Protocol::Tcp => format!(
"tcp({}, {})",
render_target_family(app.tracer_config().data.target_addr()),
app.tracer_config().data.privilege_mode()
"{}({}, {})",
t!("tcp"),
fmt_target_family(app.tracer_config().data.target_addr()),
fmt_privilege_mode(app.tracer_config().data.privilege_mode())
),
};
let details = if app.show_hop_details {
String::from("on")
String::from(t!("on"))
} else {
String::from("off")
String::from(t!("off"))
};
let as_info = match app.resolver.config().resolve_method {
ResolveMethod::System => String::from("n/a"),
ResolveMethod::System => String::from(t!("na")),
ResolveMethod::Resolv | ResolveMethod::Google | ResolveMethod::Cloudflare => {
if app.tui_config.lookup_as_info {
String::from("on")
String::from(t!("on"))
} else {
String::from("off")
String::from(t!("off"))
}
}
};
let max_hosts = app
.tui_config
.max_addrs
.map_or_else(|| String::from("auto"), |m| m.to_string());
.map_or_else(|| String::from(t!("auto")), |m| m.to_string());
let privacy = if app.hide_private_hops && app.tui_config.privacy_max_ttl > 0 {
"on"
t!("on")
} else {
"off"
t!("off")
};
let source = render_source(app);
let dest = render_destination(app);
let target = format!("{source} -> {dest}");
let hop_count = app.tracer_data().hops_for_flow(app.selected_flow).len();
let discovered = if app.selected_tracer_data.max_flows() > 1 {
let plural_flows = if app.tracer_data().flows().len() > 1 {
"flows"
t!("flows")
} else {
"flow"
t!("flow")
};
let flow_count = app.tracer_data().flows().len();
format!(
", discovered {} hops and {} unique {}",
app.tracer_data().hops_for_flow(app.selected_flow).len(),
app.tracer_data().flows().len(),
plural_flows
", {}",
t!("discovered_flows",
"hop_count" => hop_count,
"flow_count" => flow_count,
"plural_flows" => plural_flows
)
)
} else {
format!(
", discovered {} hops",
app.tracer_data().hops_for_flow(app.selected_flow).len()
)
format!(", {}", t!("discovered", "hop_count" => hop_count))
};
let left_line = vec![
Line::from(vec![
Span::styled("Target: ", Style::default().add_modifier(Modifier::BOLD)),
Span::styled(
format!("{}: ", t!("target")),
Style::default().add_modifier(Modifier::BOLD),
),
Span::raw(target),
]),
Line::from(vec![
Span::styled("Config: ", Style::default().add_modifier(Modifier::BOLD)),
Span::styled(
format!("{}: ", t!("config")),
Style::default().add_modifier(Modifier::BOLD),
),
Span::raw(format!(
"protocol={protocol} as-info={as_info} details={details} max-hosts={max_hosts}, privacy={privacy}"
"{}={protocol} {}={as_info} {}={details} {}={max_hosts}, {}={privacy}",
t!("protocol"),
t!("as-info"),
t!("details"),
t!("max-hosts"),
t!("privacy")
)),
]),
Line::from(vec![
Span::styled("Status: ", Style::default().add_modifier(Modifier::BOLD)),
Span::styled(
format!("{}: ", t!("status")),
Style::default().add_modifier(Modifier::BOLD),
),
Span::raw(render_status(app)),
Span::raw(discovered),
]),
Expand All @@ -129,7 +150,14 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
f.render_widget(left, rect);
}

const fn render_target_family(target: IpAddr) -> &'static str {
fn fmt_privilege_mode(privilege_mode: PrivilegeMode) -> Cow<'static, str> {
match privilege_mode {
PrivilegeMode::Privileged => t!("privileged"),
PrivilegeMode::Unprivileged => t!("unprivileged"),
}
}

const fn fmt_target_family(target: IpAddr) -> &'static str {
match target {
IpAddr::V4(_) => "v4",
IpAddr::V6(_) => "v6",
Expand All @@ -152,7 +180,7 @@ fn render_source(app: &TuiApp) -> String {
}
}
} else {
String::from("unknown")
String::from(t!("unknown"))
}
}

Expand Down Expand Up @@ -193,18 +221,19 @@ fn render_status(app: &TuiApp) -> String {
} else {
0_f64
};
// TODO
format!(" [{failure_count} of {total_probes} ({failure_rate:.1}%) probes failed❗]")
} else {
String::new()
};
if app.selected_tracer_data.error().is_some() {
String::from("Failed")
String::from(t!("status_failed"))
} else if let Some(start) = app.frozen_start {
let frozen = format_duration(Duration::from_secs(
start.elapsed().unwrap_or_default().as_secs(),
));
format!("Frozen ({frozen}){failures}")
format!("{} ({frozen}){failures}", t!("status_frozen"))
} else {
format!("Running{failures}")
format!("{}{failures}", t!("status_running"))
}
}
3 changes: 2 additions & 1 deletion crates/trippy-tui/src/frontend/render/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use ratatui::style::Style;
use ratatui::text::Line;
use ratatui::widgets::{Block, BorderType, Borders, Clear, Paragraph};
use ratatui::Frame;
use rust_i18n::t;

/// Render help dialog.
pub fn render(f: &mut Frame<'_>, app: &TuiApp) {
Expand Down Expand Up @@ -33,7 +34,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp) {
r#" Copyright 2022 Trippy Contributors "#.to_string(),
];
let block = Block::default()
.title(" Help ")
.title(format!(" {} ", t!("title_help")))
.title_alignment(Alignment::Center)
.borders(Borders::ALL)
.style(Style::default().bg(app.tui_config.theme.help_dialog_bg))
Expand Down
3 changes: 2 additions & 1 deletion crates/trippy-tui/src/frontend/render/histogram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ratatui::layout::Rect;
use ratatui::style::{Modifier, Style};
use ratatui::widgets::{BarChart, Block, BorderType, Borders};
use ratatui::Frame;
use rust_i18n::t;
use std::collections::BTreeMap;
use std::time::Duration;

Expand All @@ -14,7 +15,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
let barchart = BarChart::default()
.block(
Block::default()
.title(format!("Frequency #{}", selected_hop.ttl()))
.title(format!("{} #{}", t!("title_frequency"), selected_hop.ttl()))
.style(
Style::default()
.bg(app.tui_config.theme.bg)
Expand Down
3 changes: 2 additions & 1 deletion crates/trippy-tui/src/frontend/render/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ratatui::layout::Rect;
use ratatui::style::Style;
use ratatui::widgets::{Block, BorderType, Borders};
use ratatui::Frame;
use rust_i18n::t;

/// Render the ping history for the final hop which is typically the target.
pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
Expand All @@ -23,7 +24,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
let history = Sparkline::default()
.block(
Block::default()
.title(format!("Samples #{}", selected_hop.ttl()))
.title(format!("{} #{}", t!("title_samples"), selected_hop.ttl()))
.style(
Style::default()
.bg(app.tui_config.theme.bg)
Expand Down
20 changes: 11 additions & 9 deletions crates/trippy-tui/src/frontend/render/splash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use ratatui::style::Style;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, BorderType, Borders, Paragraph};
use ratatui::Frame;
use rust_i18n::t;
use std::borrow::Cow;

/// Render the splash screen.
///
Expand All @@ -13,7 +15,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
.constraints([Constraint::Percentage(35), Constraint::Percentage(65)].as_ref())
.split(rect);
let block = Block::default()
.title("Hops")
.title(t!("title_hops").to_string())
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(app.tui_config.theme.border))
Expand All @@ -23,14 +25,14 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
.fg(app.tui_config.theme.text),
);
#[allow(clippy::needless_raw_string_hashes)]
let splash = vec![
r#" _____ _ "#,
r#"|_ _| _(_)_ __ _ __ _ _ "#,
r#" | || '_| | '_ \ '_ \ || |"#,
r#" |_||_| |_| .__/ .__/\_, |"#,
r#" |_| |_| |__/ "#,
"",
"Awaiting data...",
let splash: Vec<Cow<'static, str>> = vec![
r#" _____ _ "#.into(),
r#"|_ _| _(_)_ __ _ __ _ _ "#.into(),
r#" | || '_| | '_ \ '_ \ || |"#.into(),
r#" |_||_| |_| .__/ .__/\_, |"#.into(),
r#" |_| |_| |__/ "#.into(),
"".into(),
t!("awaiting_data"),
];
let line: Vec<_> = splash
.into_iter()
Expand Down
3 changes: 2 additions & 1 deletion crates/trippy-tui/src/frontend/render/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use ratatui::layout::Rect;
use ratatui::style::{Modifier, Style};
use ratatui::widgets::{Block, BorderType, Borders, Cell, Row, Table};
use ratatui::Frame;
use rust_i18n::t;
use std::net::IpAddr;
use std::rc::Rc;
use trippy_core::{Extension, Extensions, IcmpPacketType, MplsLabelStackMember, UnknownExtension};
Expand Down Expand Up @@ -63,7 +64,7 @@ pub fn render(f: &mut Frame<'_>, app: &mut TuiApp, rect: Rect) {
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(app.tui_config.theme.border))
.title("Hops"),
.title(t!("title_hops").to_string()),
)
.style(
Style::default()
Expand Down
3 changes: 2 additions & 1 deletion crates/trippy-tui/src/frontend/render/tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, BorderType, Borders, Tabs};
use ratatui::Frame;
use rust_i18n::t;

/// Render the tabs, one per trace.
pub fn render(f: &mut Frame<'_>, rect: Rect, app: &TuiApp) {
let tabs_block = Block::default()
.title("Traces")
.title(t!("title_traces").to_string())
.title_alignment(Alignment::Left)
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
Expand Down
Loading

0 comments on commit 8845ab3

Please sign in to comment.