Repository: fujiapple852/trippy (language: rust)

Issue:
Add support for i18n
- Add support for setting the locale with `--tui-locale`
- Add `en` locale
- Add support for detecting the current `locale`
- Replace all hardcoded text strings with the `t!()` macro


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 173c1dcd0..36d884815 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -114,6 +114,12 @@ version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
 
+[[package]]
+name = "arc-swap"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
+
 [[package]]
 name = "arrayvec"
 version = "0.7.6"
@@ -152,18 +158,40 @@ dependencies = [
  "rustc-demangle",
 ]
 
+[[package]]
+name = "base62"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f879ef8fc74665ed7f0e6127cb106315888fc2744f68e14b74f83edbb2a08992"
+
 [[package]]
 name = "base64"
 version = "0.22.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
 
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
 [[package]]
 name = "bitflags"
 version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
+[[package]]
+name = "bstr"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
 [[package]]
 name = "bumpalo"
 version = "3.16.0"
@@ -335,7 +363,7 @@ checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7"
 dependencies = [
  "strum",
  "strum_macros",
- "unicode-width",
+ "unicode-width 0.1.13",
 ]
 
 [[package]]
@@ -432,7 +460,7 @@ version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
 dependencies = [
- "bitflags",
+ "bitflags 2.6.0",
  "crossterm_winapi",
  "mio",
  "parking_lot",
@@ -636,6 +664,12 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "fastrand"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
+
 [[package]]
 name = "fixedbitset"
 version = "0.4.2"
@@ -726,6 +760,36 @@ version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
 
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "globset"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata 0.4.7",
+ "regex-syntax 0.8.4",
+]
+
+[[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 = "hashbrown"
 version = "0.12.3"
@@ -900,6 +964,22 @@ dependencies = [
  "unicode-normalization",
 ]
 
+[[package]]
+name = "ignore"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
+dependencies = [
+ "crossbeam-deque",
+ "globset",
+ "log",
+ "memchr",
+ "regex-automata 0.4.7",
+ "same-file",
+ "walkdir",
+ "winapi-util",
+]
+
 [[package]]
 name = "indexmap"
 version = "1.9.3"
@@ -978,6 +1058,15 @@ version = "1.70.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
 
+[[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"
@@ -1024,6 +1113,12 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "libyml"
+version = "0.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64804cc6a5042d4f05379909ba25b503ec04e2c082151d62122d5dcaa274b961"
+
 [[package]]
 name = "linked-hash-map"
 version = "0.5.6"
@@ -1166,13 +1261,22 @@ version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
 dependencies = [
- "bitflags",
+ "bitflags 2.6.0",
  "cfg-if",
  "cfg_aliases",
  "libc",
  "memoffset",
 ]
 
+[[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 = "nu-ansi-term"
 version = "0.46.0"
@@ -1387,19 +1491,19 @@ version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d"
 dependencies = [
- "bitflags",
+ "bitflags 2.6.0",
  "cassowary",
  "compact_str",
  "crossterm",
  "instability",
- "itertools",
+ "itertools 0.13.0",
  "lru",
  "paste",
  "strum",
  "strum_macros",
  "unicode-segmentation",
  "unicode-truncate",
- "unicode-width",
+ "unicode-width 0.1.13",
 ]
 
 [[package]]
@@ -1408,7 +1512,7 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
 dependencies = [
- "bitflags",
+ "bitflags 2.6.0",
 ]
 
 [[package]]
@@ -1471,6 +1575,60 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
 
+[[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 2.0.75",
+]
+
+[[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",
+ "toml 0.7.8",
+ "triomphe",
+]
+
 [[package]]
 name = "rustc-demangle"
 version = "0.1.24"
@@ -1483,7 +1641,7 @@ version = "0.38.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
 dependencies = [
- "bitflags",
+ "bitflags 2.6.0",
  "errno",
  "libc",
  "linux-raw-sys",
@@ -1502,6 +1660,15 @@ version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
+[[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 = "scopeguard"
 version = "1.2.0"
@@ -1592,6 +1759,23 @@ dependencies = [
  "unsafe-libyaml",
 ]
 
+[[package]]
+name = "serde_yml"
+version = "0.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c"
+dependencies = [
+ "indexmap 2.5.0",
+ "itoa",
+ "libyml",
+ "log",
+ "memchr",
+ "ryu",
+ "serde",
+ "serde_json",
+ "tempfile",
+]
+
 [[package]]
 name = "sharded-slab"
 version = "0.1.7"
@@ -1643,6 +1827,12 @@ version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
 
+[[package]]
+name = "siphasher"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -1668,6 +1858,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
 [[package]]
 name = "static_assertions"
 version = "1.1.0"
@@ -1724,6 +1920,28 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "sys-locale"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
 [[package]]
 name = "terminal_size"
 version = "0.3.0"
@@ -1891,6 +2109,18 @@ dependencies = [
  "tokio",
 ]
 
+[[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 0.19.15",
+]
+
 [[package]]
 name = "toml"
 version = "0.8.19"
@@ -1900,7 +2130,7 @@ dependencies = [
  "serde",
  "serde_spanned",
  "toml_datetime",
- "toml_edit",
+ "toml_edit 0.22.20",
 ]
 
 [[package]]
@@ -1912,6 +2142,19 @@ 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.5.0",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow 0.5.40",
+]
+
 [[package]]
 name = "toml_edit"
 version = "0.22.20"
@@ -1922,7 +2165,7 @@ dependencies = [
  "serde",
  "serde_spanned",
  "toml_datetime",
- "winnow",
+ "winnow 0.6.18",
 ]
 
 [[package]]
@@ -1931,7 +2174,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "clap",
- "itertools",
+ "itertools 0.13.0",
  "trippy",
 ]
 
@@ -2020,6 +2263,17 @@ dependencies = [
  "tracing-serde",
 ]
 
+[[package]]
+name = "triomphe"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369"
+dependencies = [
+ "arc-swap",
+ "serde",
+ "stable_deref_trait",
+]
+
 [[package]]
 name = "trippy"
 version = "0.12.0-dev"
@@ -2038,12 +2292,12 @@ version = "0.12.0-dev"
 dependencies = [
  "anyhow",
  "arrayvec",
- "bitflags",
+ "bitflags 2.6.0",
  "derive_more",
  "hex-literal",
  "indexmap 2.5.0",
  "ipnetwork",
- "itertools",
+ "itertools 0.13.0",
  "mockall",
  "nix",
  "parking_lot",
@@ -2073,7 +2327,7 @@ dependencies = [
  "crossbeam",
  "dns-lookup",
  "hickory-resolver",
- "itertools",
+ "itertools 0.13.0",
  "parking_lot",
  "thiserror",
 ]
@@ -2084,7 +2338,7 @@ version = "0.12.0-dev"
 dependencies = [
  "anyhow",
  "hex-literal",
- "itertools",
+ "itertools 0.13.0",
  "thiserror",
 ]
 
@@ -2117,25 +2371,28 @@ dependencies = [
  "etcetera",
  "humantime",
  "insta",
- "itertools",
+ "itertools 0.13.0",
  "maxminddb",
  "petgraph",
  "pretty_assertions",
  "ratatui",
+ "rust-i18n",
  "serde",
  "serde_json",
  "serde_with",
  "serde_yaml",
  "strum",
+ "sys-locale",
  "test-case",
  "thiserror",
- "toml",
+ "toml 0.8.19",
  "tracing",
  "tracing-chrome",
  "tracing-subscriber",
  "trippy-core",
  "trippy-dns",
  "trippy-privilege",
+ "unicode-width 0.2.0",
 ]
 
 [[package]]
@@ -2193,9 +2450,9 @@ 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",
+ "unicode-width 0.1.13",
 ]
 
 [[package]]
@@ -2204,6 +2461,12 @@ version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
 
+[[package]]
+name = "unicode-width"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+
 [[package]]
 name = "unsafe-libyaml"
 version = "0.2.11"
@@ -2239,6 +2502,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 = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
@@ -2322,6 +2595,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"
@@ -2485,6 +2767,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 = "winnow"
 version = "0.6.18"
diff --git a/Cargo.toml b/Cargo.toml
index d5bdd4fb0..5dd5fab74 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -61,12 +61,14 @@ petgraph = "0.6.5"
 pretty_assertions = "1.4.1"
 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"
 serde_yaml = "=0.9.33"
 socket2 = "0.5.7"
 strum = { version = "0.26.3", default-features = false }
+sys-locale = "0.3.1"
 test-case = "3.3.1"
 thiserror = "1.0.60"
 tokio = "1.40.0"
@@ -76,6 +78,7 @@ tracing = "0.1.40"
 tracing-chrome = "0.7.2"
 tracing-subscriber = { version = "0.3.18", default-features = false }
 tun2 = "2.0.9"
+unicode-width = "0.2.0"
 widestring = "1.0.2"
 windows-sys = "0.52.0"
 
diff --git a/crates/trippy-tui/Cargo.toml b/crates/trippy-tui/Cargo.toml
index 147f70483..27af5e7dd 100644
--- a/crates/trippy-tui/Cargo.toml
+++ b/crates/trippy-tui/Cargo.toml
@@ -42,15 +42,18 @@ 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
 strum = { workspace = true, default-features = false, features = [ "std", "derive" ] }
+sys-locale.workspace = true
 thiserror.workspace = true
 toml = { workspace = true, default-features = false, features = [ "parse" ] }
 tracing-chrome.workspace = true
 tracing-subscriber = { workspace = true, default-features = false, features = [ "env-filter", "json" ] }
 tracing.workspace = true
+unicode-width.workspace = true
 
 [dev-dependencies]
 insta = { workspace = true, features = ["serde"] }
diff --git a/crates/trippy-tui/locales/app.yml b/crates/trippy-tui/locales/app.yml
new file mode 100644
index 000000000..6126a37c6
--- /dev/null
+++ b/crates/trippy-tui/locales/app.yml
@@ -0,0 +1,221 @@
+_version: 2
+trippy:
+  en: "trippy"
+auto:
+  en: "auto"
+on:
+  en: "on"
+off:
+  en: "off"
+yes:
+  en: "Yes"
+no:
+  en: "No"
+none:
+  en: "none"
+hidden:
+  en: "Hidden"
+flow:
+  en: "flow"
+flows:
+  en: "flows"
+target:
+  en: "Target"
+config:
+  en: "Config"
+status:
+  en: "Status"
+protocol:
+  en: "protocol"
+as-info:
+  en: "as-info"
+details:
+  en: "details"
+max-hosts:
+  en: "max-hosts"
+privacy:
+  en: "privacy"
+privileged:
+  en: "privileged"
+unprivileged:
+  en: "unprivileged"
+na:
+  en: "n/a"
+discovered:
+  en: "discovered %{hop_count} hops"
+discovered_flows:
+  en: "discovered %{hop_count} hops and %{flow_count} unique %{plural_flows}"
+unknown:
+  en: "unknown"
+icmp:
+  en: "icmp"
+udp:
+  en: "udp"
+tcp:
+  en: "tcp"
+status_failures:
+  en: "%{failure_count} of %{total_probes} (%{failure_rate}%) probes failed"
+status_failed:
+  en: "Failed"
+status_running:
+  en: "Running"
+status_frozen:
+  en: "Frozen"
+awaiting_data:
+  en: "Awaiting data..."
+header_help:
+  en: "elp"
+header_settings:
+  en: "ettings"
+header_quit:
+  en: "uit"
+title_hops:
+  en: "Hops"
+title_frequency:
+  en: "Frequency"
+title_samples:
+  en: "Samples"
+title_traces:
+  en: "Traces"
+title_flows:
+  en: "Flows"
+title_map:
+  en: "Map"
+title_help:
+  en: "Help"
+title_settings:
+  en: "Settings"
+bsod_failed:
+  en: "Trippy Failed :("
+bsod_quit:
+  en: "Press q to quit"
+hop:
+  en: "Hop"
+rtt:
+  en: "RTT"
+title_chart:
+  en: "Chart"
+samples:
+  en: "Samples"
+host:
+  en: "Host"
+no_response:
+  en: "No response"
+dns_failed:
+  en: "Failed"
+dns_timeout:
+  en: "Timeout"
+labels:
+  en: "labels"
+not_enabled:
+  en: "not enabled"
+not_found:
+  en: "not found"
+awaited:
+  en: "awaited"
+name:
+  en: "Name"
+info:
+  en: "Info"
+geo:
+  en: "Geo"
+pos:
+  en: "Pos"
+ext:
+  en: "Ext"
+help_tagline:
+  en: "A network diagnostic tool"
+help_show_settings:
+  en: "Press [%{key}] to show all settings"
+help_show_bindings:
+  en: "Press [%{key}] to show all bindings"
+help_show_columns:
+  en: "Press [%{key}] to show all columns"
+help_license:
+  en: "Distributed under the Apache License 2.0"
+help_copyright:
+  en: "Copyright 2022 Trippy Contributors"
+geoip_not_enabled:
+  en: "GeoIp not enabled"
+geoip_no_data_for_hop:
+  en: "No GeoIp data for hop"
+geoip_multiple_data_for_hop:
+  en: "Multiple GeoIp locations for hop"
+kilometer:
+  en: "km"
+settings_info:
+  en: "Info"
+settings_tab_tui_title:
+  en: "Tui"
+settings_tab_trace_title:
+  en: "Trace"
+settings_tab_dns_title:
+  en: "DNS"
+settings_tab_geoip_title:
+  en: "GeoIp"
+settings_tab_bindings_title:
+  en: "Bindings"
+settings_tab_theme_title:
+  en: "Theme"
+settings_tab_columns_title:
+  en: "Columns"
+settings_tab_tui_desc:
+  en: "Settings which control how data is displayed in this Tui"
+settings_tab_trace_desc:
+  en: "Settings which control the tracing strategy"
+settings_tab_dns_desc:
+  en: "Settings which control how DNS lookups are performed"
+settings_tab_geoip_desc:
+  en: "Settings relating to GeoIp"
+settings_tab_bindings_desc:
+  en: "Tui key bindings"
+settings_tab_theme_desc:
+  en: "Tui theme colors"
+settings_tab_columns_desc:
+  en: "Tui table columns.  Press [%{c}] to toggle a column on or off and use the [%{d}] and [%{u}] keys to change the column order."
+settings_table_header_setting:
+  en: "Setting"
+settings_table_header_value:
+  en: "Value"
+column_host:
+  en: "Host"
+column_loss_pct:
+  en: "Loss%"
+column_snd:
+  en: "Snd"
+column_recv:
+  en: "Recv"
+column_last:
+  en: "Last"
+column_avg:
+  en: "Avg"
+column_best:
+  en: "Best"
+column_wrst:
+  en: "Wrst"
+column_stdev:
+  en: "StDev"
+column_sts:
+  en: "Sts"
+column_jttr:
+  en: "Jttr"
+column_javg:
+  en: "Javg"
+column_jmax:
+  en: "Jmax"
+column_jint:
+  en: "Jint"
+column_sprt:
+  en: "Sprt"
+column_dprt:
+  en: "Dprt"
+column_seq:
+  en: "Seq"
+column_type:
+  en: "Type"
+column_code:
+  en: "Code"
+column_nat:
+  en: "Nat"
+column_fail:
+  en: "Fail"
\ No newline at end of file
diff --git a/crates/trippy-tui/src/app.rs b/crates/trippy-tui/src/app.rs
index e0303f4d9..a0846ce8f 100644
--- a/crates/trippy-tui/src/app.rs
+++ b/crates/trippy-tui/src/app.rs
@@ -1,6 +1,7 @@
 use crate::config::{LogFormat, LogSpanEvents, Mode, TrippyConfig};
 use crate::frontend::TuiConfig;
 use crate::geoip::GeoIpLookup;
+use crate::locale::set_locale;
 use crate::{frontend, report};
 use anyhow::{anyhow, Error};
 use std::net::IpAddr;
@@ -14,6 +15,7 @@ use trippy_privilege::Privilege;
 
 /// Run the trippy application.
 pub fn run_trippy(cfg: &TrippyConfig, pid: u16) -> anyhow::Result<()> {
+    set_locale(cfg.tui_locale.as_deref());
     let _guard = configure_logging(cfg);
     let resolver = start_dns_resolver(cfg)?;
     let geoip_lookup = create_geoip_lookup(cfg)?;
diff --git a/crates/trippy-tui/src/config.rs b/crates/trippy-tui/src/config.rs
index b36012077..c62850040 100644
--- a/crates/trippy-tui/src/config.rs
+++ b/crates/trippy-tui/src/config.rs
@@ -313,6 +313,7 @@ pub struct TrippyConfig {
     pub tui_icmp_extension_mode: IcmpExtensionMode,
     pub tui_geoip_mode: GeoIpMode,
     pub tui_max_addrs: Option<u8>,
+    pub tui_locale: Option<String>,
     pub tui_theme: TuiTheme,
     pub tui_bindings: TuiBindings,
     pub mode: Mode,
@@ -537,6 +538,7 @@ impl TrippyConfig {
             cfg_file_dns.dns_resolve_method,
             constants::DEFAULT_DNS_RESOLVE_METHOD,
         );
+        let tui_locale = cfg_layer_opt(args.tui_locale, cfg_file_tui.tui_locale);
         let dns_lookup_as_info = cfg_layer_bool_flag(
             args.dns_lookup_as_info,
             cfg_file_dns.dns_lookup_as_info,
@@ -697,6 +699,7 @@ impl TrippyConfig {
             tui_icmp_extension_mode,
             tui_geoip_mode,
             tui_max_addrs,
+            tui_locale,
             tui_theme,
             tui_bindings,
             mode,
@@ -749,6 +752,7 @@ impl Default for TrippyConfig {
             tui_icmp_extension_mode: constants::DEFAULT_TUI_ICMP_EXTENSION_MODE,
             tui_geoip_mode: constants::DEFAULT_TUI_GEOIP_MODE,
             tui_max_addrs: None,
+            tui_locale: None,
             tui_theme: TuiTheme::default(),
             tui_bindings: TuiBindings::default(),
             mode: constants::DEFAULT_MODE,
@@ -1465,6 +1469,12 @@ mod tests {
         compare(parse_config(cmd), expected);
     }
 
+    #[test_case("trip example.com", Ok(cfg().tui_locale(None).build()); "default tui locale")]
+    #[test_case("trip example.com --tui-locale fr", Ok(cfg().tui_locale(Some(String::from("fr"))).build()); "custom tui locale")]
+    fn test_tui_locale(cmd: &str, expected: anyhow::Result<TrippyConfig>) {
+        compare(parse_config(cmd), expected);
+    }
+
     #[test_case("trip example.com", Ok(cfg().tui_address_mode(AddressMode::Host).build()); "default tui address mode")]
     #[test_case("trip example.com --tui-address-mode ip", Ok(cfg().tui_address_mode(AddressMode::IP).build()); "ip tui address mode")]
     #[test_case("trip example.com --tui-address-mode host", Ok(cfg().tui_address_mode(AddressMode::Host).build()); "host tui address mode")]
@@ -2040,6 +2050,15 @@ mod tests {
             }
         }
 
+        pub fn tui_locale(self, tui_locale: Option<String>) -> Self {
+            Self {
+                config: TrippyConfig {
+                    tui_locale,
+                    ..self.config
+                },
+            }
+        }
+
         pub fn tui_address_mode(self, tui_address_mode: AddressMode) -> Self {
             Self {
                 config: TrippyConfig {
diff --git a/crates/trippy-tui/src/config/cmd.rs b/crates/trippy-tui/src/config/cmd.rs
index 8f4054cc7..df6f1c1d6 100644
--- a/crates/trippy-tui/src/config/cmd.rs
+++ b/crates/trippy-tui/src/config/cmd.rs
@@ -220,6 +220,10 @@ pub struct Args {
     #[arg(long)]
     pub tui_privacy_max_ttl: Option<u8>,
 
+    /// The locale to use for the TUI [default: auto]
+    #[arg(long)]
+    pub tui_locale: Option<String>,
+
     /// The TUI theme colors [item=color,item=color,..]
     #[arg(long, value_delimiter(','), value_parser = parse_tui_theme_color_value)]
     pub tui_theme_colors: Vec<(TuiThemeItem, TuiColor)>,
diff --git a/crates/trippy-tui/src/config/file.rs b/crates/trippy-tui/src/config/file.rs
index 839eab456..9edafab6b 100644
--- a/crates/trippy-tui/src/config/file.rs
+++ b/crates/trippy-tui/src/config/file.rs
@@ -247,6 +247,7 @@ pub struct ConfigTui {
     pub tui_max_addrs: Option<u8>,
     pub geoip_mmdb_file: Option<String>,
     pub tui_custom_columns: Option<String>,
+    pub tui_locale: Option<String>,
     #[serde(rename = "tui-max-samples")]
     pub deprecated_tui_max_samples: Option<usize>,
     #[serde(rename = "tui-max-flows")]
@@ -265,6 +266,7 @@ impl Default for ConfigTui {
             tui_icmp_extension_mode: Some(super::constants::DEFAULT_TUI_ICMP_EXTENSION_MODE),
             tui_geoip_mode: Some(super::constants::DEFAULT_TUI_GEOIP_MODE),
             tui_max_addrs: Some(super::constants::DEFAULT_TUI_MAX_ADDRS),
+            tui_locale: None,
             geoip_mmdb_file: None,
             deprecated_tui_max_samples: None,
             deprecated_tui_max_flows: None,
diff --git a/crates/trippy-tui/src/frontend/columns.rs b/crates/trippy-tui/src/frontend/columns.rs
index de279521d..ffa48dafe 100644
--- a/crates/trippy-tui/src/frontend/columns.rs
+++ b/crates/trippy-tui/src/frontend/columns.rs
@@ -1,7 +1,10 @@
 use crate::config::{TuiColumn, TuiColumns};
+use crate::t;
 use ratatui::layout::{Constraint, Rect};
-use std::fmt::{Display, Formatter};
+use std::borrow::Cow;
+use std::fmt::{Debug, Display, Formatter};
 use strum::{EnumIter, IntoEnumIterator};
+use unicode_width::UnicodeWidthStr;
 
 /// The columns to display in the hops table of the TUI.
 #[derive(Debug, Clone, Eq, PartialEq)]
@@ -133,8 +136,8 @@ pub enum ColumnStatus {
 impl Display for ColumnStatus {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         match self {
-            Self::Shown => write!(f, "on"),
-            Self::Hidden => write!(f, "off"),
+            Self::Shown => write!(f, "{}", t!("on")),
+            Self::Hidden => write!(f, "{}", t!("off")),
         }
     }
 }
@@ -248,60 +251,74 @@ impl From<TuiColumn> for Column {
 
 impl Display for ColumnType {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Self::Ttl => write!(f, "#"),
-            Self::Host => write!(f, "Host"),
-            Self::LossPct => write!(f, "Loss%"),
-            Self::Sent => write!(f, "Snd"),
-            Self::Received => write!(f, "Recv"),
-            Self::Last => write!(f, "Last"),
-            Self::Average => write!(f, "Avg"),
-            Self::Best => write!(f, "Best"),
-            Self::Worst => write!(f, "Wrst"),
-            Self::StdDev => write!(f, "StDev"),
-            Self::Status => write!(f, "Sts"),
-            Self::Jitter => write!(f, "Jttr"),
-            Self::Javg => write!(f, "Javg"),
-            Self::Jmax => write!(f, "Jmax"),
-            Self::Jinta => write!(f, "Jint"),
-            Self::LastSrcPort => write!(f, "Sprt"),
-            Self::LastDestPort => write!(f, "Dprt"),
-            Self::LastSeq => write!(f, "Seq"),
-            Self::LastIcmpPacketType => write!(f, "Type"),
-            Self::LastIcmpPacketCode => write!(f, "Code"),
-            Self::LastNatStatus => write!(f, "Nat"),
-            Self::Failed => write!(f, "Fail"),
-        }
+        write!(f, "{}", self.name())
     }
 }
 
 impl ColumnType {
+    /// The name of the column in the current locale.
+    pub(self) fn name(&self) -> Cow<'_, str> {
+        match self {
+            Self::Ttl => Cow::Borrowed("#"),
+            Self::Host => t!("column_host"),
+            Self::LossPct => t!("column_loss_pct"),
+            Self::Sent => t!("column_snd"),
+            Self::Received => t!("column_recv"),
+            Self::Last => t!("column_last"),
+            Self::Average => t!("column_avg"),
+            Self::Best => t!("column_best"),
+            Self::Worst => t!("column_wrst"),
+            Self::StdDev => t!("column_stdev"),
+            Self::Status => t!("column_sts"),
+            Self::Jitter => t!("column_jttr"),
+            Self::Javg => t!("column_javg"),
+            Self::Jmax => t!("column_jmax"),
+            Self::Jinta => t!("column_jint"),
+            Self::LastSrcPort => t!("column_sprt"),
+            Self::LastDestPort => t!("column_dprt"),
+            Self::LastSeq => t!("column_seq"),
+            Self::LastIcmpPacketType => t!("column_type"),
+            Self::LastIcmpPacketCode => t!("column_code"),
+            Self::LastNatStatus => t!("column_nat"),
+            Self::Failed => t!("column_fail"),
+        }
+    }
+
     /// The width of the column.
-    pub(self) const fn width(self) -> ColumnWidth {
+    ///
+    /// For most columns the width is calculated based on the column name in
+    /// the current locale.
+    ///
+    /// For the `Ttl` column the width is fixed as it is always a single
+    /// character.
+    ///
+    /// The `Host` column is variable as it should use the remaining space.
+    pub(self) fn width(self) -> ColumnWidth {
+        let width = self.name().width() as u16 + 2;
         #[allow(clippy::match_same_arms)]
         match self {
             Self::Ttl => ColumnWidth::Fixed(4),
             Self::Host => ColumnWidth::Variable,
-            Self::LossPct => ColumnWidth::Fixed(8),
-            Self::Sent => ColumnWidth::Fixed(7),
-            Self::Received => ColumnWidth::Fixed(7),
-            Self::Last => ColumnWidth::Fixed(7),
-            Self::Average => ColumnWidth::Fixed(7),
-            Self::Best => ColumnWidth::Fixed(7),
-            Self::Worst => ColumnWidth::Fixed(7),
-            Self::StdDev => ColumnWidth::Fixed(8),
-            Self::Status => ColumnWidth::Fixed(7),
-            Self::Jitter => ColumnWidth::Fixed(7),
-            Self::Javg => ColumnWidth::Fixed(7),
-            Self::Jmax => ColumnWidth::Fixed(7),
-            Self::Jinta => ColumnWidth::Fixed(8),
-            Self::LastSrcPort => ColumnWidth::Fixed(7),
-            Self::LastDestPort => ColumnWidth::Fixed(7),
-            Self::LastSeq => ColumnWidth::Fixed(7),
-            Self::LastIcmpPacketType => ColumnWidth::Fixed(7),
-            Self::LastIcmpPacketCode => ColumnWidth::Fixed(7),
-            Self::LastNatStatus => ColumnWidth::Fixed(7),
-            Self::Failed => ColumnWidth::Fixed(7),
+            Self::LossPct => ColumnWidth::Fixed(width.max(8)),
+            Self::Sent => ColumnWidth::Fixed(width.max(7)),
+            Self::Received => ColumnWidth::Fixed(width.max(7)),
+            Self::Last => ColumnWidth::Fixed(width.max(7)),
+            Self::Average => ColumnWidth::Fixed(width.max(7)),
+            Self::Best => ColumnWidth::Fixed(width.max(7)),
+            Self::Worst => ColumnWidth::Fixed(width.max(7)),
+            Self::StdDev => ColumnWidth::Fixed(width.max(8)),
+            Self::Status => ColumnWidth::Fixed(width.max(7)),
+            Self::Jitter => ColumnWidth::Fixed(width.max(7)),
+            Self::Javg => ColumnWidth::Fixed(width.max(7)),
+            Self::Jmax => ColumnWidth::Fixed(width.max(7)),
+            Self::Jinta => ColumnWidth::Fixed(width.max(8)),
+            Self::LastSrcPort => ColumnWidth::Fixed(width.max(7)),
+            Self::LastDestPort => ColumnWidth::Fixed(width.max(7)),
+            Self::LastSeq => ColumnWidth::Fixed(width.max(7)),
+            Self::LastIcmpPacketType => ColumnWidth::Fixed(width.max(7)),
+            Self::LastIcmpPacketCode => ColumnWidth::Fixed(width.max(7)),
+            Self::LastNatStatus => ColumnWidth::Fixed(width.max(7)),
+            Self::Failed => ColumnWidth::Fixed(width.max(7)),
         }
     }
 }
diff --git a/crates/trippy-tui/src/frontend/render/bsod.rs b/crates/trippy-tui/src/frontend/render/bsod.rs
index 0907b8ada..a56df5c11 100644
--- a/crates/trippy-tui/src/frontend/render/bsod.rs
+++ b/crates/trippy-tui/src/frontend/render/bsod.rs
@@ -1,3 +1,4 @@
+use crate::t;
 use ratatui::layout::{Alignment, Constraint, Layout, Rect};
 use ratatui::style::{Color, Modifier, Style};
 use ratatui::text::{Line, Span};
@@ -10,19 +11,19 @@ pub fn render(f: &mut Frame<'_>, rect: Rect, error: &str) {
         .constraints([Constraint::Percentage(35), Constraint::Percentage(65)].as_ref())
         .split(rect);
     let block = Block::default()
-        .title("Hops")
+        .title(Line::raw(t!("title_hops")))
         .borders(Borders::ALL)
         .border_type(BorderType::Rounded)
         .style(Style::default().bg(Color::Blue));
     let line = vec![
         Line::from(Span::styled(
-            "Trippy Failed :(",
+            t!("bsod_failed"),
             Style::default().add_modifier(Modifier::REVERSED),
         )),
         Line::from(""),
         Line::from(error),
         Line::from(""),
-        Line::from("Press q to quit "),
+        Line::raw(t!("bsod_quit")),
     ];
     let paragraph = Paragraph::new(line).alignment(Alignment::Center);
     f.render_widget(block, rect);
diff --git a/crates/trippy-tui/src/frontend/render/chart.rs b/crates/trippy-tui/src/frontend/render/chart.rs
index ed2b9a1b3..25ef4505b 100644
--- a/crates/trippy-tui/src/frontend/render/chart.rs
+++ b/crates/trippy-tui/src/frontend/render/chart.rs
@@ -1,8 +1,9 @@
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use ratatui::layout::{Alignment, Constraint, Rect};
 use ratatui::style::Style;
 use ratatui::symbols::Marker;
-use ratatui::text::Span;
+use ratatui::text::{Line, Span};
 use ratatui::widgets::{Axis, Block, BorderType, Borders, Chart, Dataset, GraphType};
 use ratatui::Frame;
 
@@ -34,7 +35,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
         .enumerate()
         .map(|(i, s)| {
             Dataset::default()
-                .name(format!("Hop {}", i + 1))
+                .name(format!("{} {}", t!("hop"), i + 1))
                 .data(s)
                 .graph_type(GraphType::Line)
                 .marker(Marker::Braille)
@@ -52,7 +53,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
     let chart = Chart::new(sets)
         .x_axis(
             Axis::default()
-                .title("Samples")
+                .title(Line::raw(t!("samples")))
                 .bounds([0_f64, samples as f64])
                 .labels_alignment(Alignment::Right)
                 .labels(
@@ -64,7 +65,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
         )
         .y_axis(
             Axis::default()
-                .title("RTT")
+                .title(Line::raw(t!("rtt")))
                 .bounds([0_f64, max_sample])
                 .labels(
                     [
@@ -88,7 +89,7 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
                 .borders(Borders::ALL)
                 .border_type(BorderType::Rounded)
                 .border_style(Style::default().fg(app.tui_config.theme.border))
-                .title("Chart"),
+                .title(Line::raw(t!("chart"))),
         );
     f.render_widget(chart, rect);
 }
diff --git a/crates/trippy-tui/src/frontend/render/flows.rs b/crates/trippy-tui/src/frontend/render/flows.rs
index ec66b0535..7982a5d10 100644
--- a/crates/trippy-tui/src/frontend/render/flows.rs
+++ b/crates/trippy-tui/src/frontend/render/flows.rs
@@ -1,4 +1,5 @@
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use ratatui::layout::{Alignment, Rect};
 use ratatui::style::{Modifier, Style};
 use ratatui::text::Line;
@@ -35,7 +36,7 @@ pub fn render(f: &mut Frame<'_>, rect: Rect, app: &TuiApp) {
         })
         .collect();
     let block = Block::default()
-        .title("Flows")
+        .title(Line::raw(t!("title_flows")))
         .title_alignment(Alignment::Left)
         .borders(Borders::ALL)
         .border_type(BorderType::Rounded)
diff --git a/crates/trippy-tui/src/frontend/render/header.rs b/crates/trippy-tui/src/frontend/render/header.rs
index a48e8d190..eed331285 100644
--- a/crates/trippy-tui/src/frontend/render/header.rs
+++ b/crates/trippy-tui/src/frontend/render/header.rs
@@ -1,4 +1,5 @@
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use chrono::SecondsFormat;
 use humantime::format_duration;
 use ratatui::layout::{Alignment, Rect};
@@ -6,16 +7,17 @@ use ratatui::style::{Modifier, Style};
 use ratatui::text::{Line, Span};
 use ratatui::widgets::{Block, BorderType, Borders, Paragraph};
 use ratatui::Frame;
+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)
@@ -29,11 +31,11 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
     let clock_span = Line::from(Span::raw(now));
     let help_span = Line::from(vec![
         Span::styled("h", Style::default().add_modifier(Modifier::BOLD)),
-        Span::raw("elp "),
-        Span::styled("s", Style::default().add_modifier(Modifier::BOLD)),
-        Span::raw("ettings "),
-        Span::styled("q", Style::default().add_modifier(Modifier::BOLD)),
-        Span::raw("uit"),
+        Span::raw(t!("header_help")),
+        Span::styled(" s", Style::default().add_modifier(Modifier::BOLD)),
+        Span::raw(t!("header_settings")),
+        Span::styled(" q", Style::default().add_modifier(Modifier::BOLD)),
+        Span::raw(t!("header_quit")),
     ]);
     let right_line = vec![clock_span, help_span];
     let right = Paragraph::new(right_line)
@@ -42,80 +44,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),
         ]),
@@ -129,7 +149,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",
@@ -152,7 +179,7 @@ fn render_source(app: &TuiApp) -> String {
             }
         }
     } else {
-        String::from("unknown")
+        String::from(t!("unknown"))
     }
 }
 
@@ -193,18 +220,25 @@ fn render_status(app: &TuiApp) -> String {
         } else {
             0_f64
         };
-        format!(" [{failure_count} of {total_probes} ({failure_rate:.1}%) probes failed❗]")
+        let failure_rate = format!("{failure_rate:.1}");
+        format!(
+            " [{}❗]",
+            t!("status_failures",
+                    "failure_count" => failure_count,
+                    "total_probes" => total_probes,
+                    "failure_rate" => failure_rate)
+        )
     } 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"))
     }
 }
diff --git a/crates/trippy-tui/src/frontend/render/help.rs b/crates/trippy-tui/src/frontend/render/help.rs
index 57766341c..b37cd8a18 100644
--- a/crates/trippy-tui/src/frontend/render/help.rs
+++ b/crates/trippy-tui/src/frontend/render/help.rs
@@ -1,5 +1,6 @@
 use crate::frontend::render::util;
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use ratatui::layout::Alignment;
 use ratatui::style::Style;
 use ratatui::text::Line;
@@ -13,36 +14,32 @@ pub fn render(f: &mut Frame<'_>, app: &TuiApp) {
     let c = app.tui_config.bindings.toggle_settings_columns;
     #[allow(clippy::needless_raw_string_hashes)]
     let help_lines = vec![
-        r#"                           "#.to_string(),
-        r#" _____    _                "#.to_string(),
-        r#"|_   _| _(_)_ __ _ __ _  _ "#.to_string(),
-        r#"  | || '_| | '_ \ '_ \ || |"#.to_string(),
-        r#"  |_||_| |_| .__/ .__/\_, |"#.to_string(),
-        r#"           |_|  |_|   |__/ "#.to_string(),
-        r#"                           "#.to_string(),
-        r#" A network diagnostic tool "#.to_string(),
-        r#"                           "#.to_string(),
-        format!(" Press [{s}] to show all settings "),
-        format!(" Press [{b}] to show key bindings "),
-        format!(" Press [{c}] to choose columns "),
-        r#"                           "#.to_string(),
-        r#" https://github.com/fujiapple852/trippy "#.to_string(),
-        r#"                           "#.to_string(),
-        r#" Distributed under the Apache License 2.0 "#.to_string(),
-        r#"                           "#.to_string(),
-        r#" Copyright 2022 Trippy Contributors "#.to_string(),
+        Line::raw(r#"                           "#),
+        Line::raw(r#" _____    _                "#),
+        Line::raw(r#"|_   _| _(_)_ __ _ __ _  _ "#),
+        Line::raw(r#"  | || '_| | '_ \ '_ \ || |"#),
+        Line::raw(r#"  |_||_| |_| .__/ .__/\_, |"#),
+        Line::raw(r#"           |_|  |_|   |__/ "#),
+        Line::raw(r#"                           "#),
+        Line::raw(t!("help_tagline")),
+        Line::raw(r#"                           "#),
+        Line::raw(t!("help_show_settings", key = s)),
+        Line::raw(t!("help_show_bindings", key = b)),
+        Line::raw(t!("help_show_columns", key = c)),
+        Line::raw(r#"                           "#),
+        Line::raw(r#" https://github.com/fujiapple852/trippy "#),
+        Line::raw(r#"                           "#),
+        Line::raw(t!("help_license")),
+        Line::raw(r#"                           "#),
+        Line::raw(t!("help_copyright")),
     ];
     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))
         .border_type(BorderType::Double);
-    let control_line: Vec<_> = help_lines
-        .iter()
-        .map(|line| Line::from(line.as_str()))
-        .collect();
-    let control = Paragraph::new(control_line)
+    let control = Paragraph::new(help_lines)
         .style(Style::default().fg(app.tui_config.theme.help_dialog_text))
         .block(block.clone())
         .alignment(Alignment::Center);
diff --git a/crates/trippy-tui/src/frontend/render/histogram.rs b/crates/trippy-tui/src/frontend/render/histogram.rs
index 8651275d5..432038180 100644
--- a/crates/trippy-tui/src/frontend/render/histogram.rs
+++ b/crates/trippy-tui/src/frontend/render/histogram.rs
@@ -1,4 +1,5 @@
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use ratatui::layout::Rect;
 use ratatui::style::{Modifier, Style};
 use ratatui::widgets::{BarChart, Block, BorderType, Borders};
@@ -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)
diff --git a/crates/trippy-tui/src/frontend/render/history.rs b/crates/trippy-tui/src/frontend/render/history.rs
index d1e0f30d0..19d5e037f 100644
--- a/crates/trippy-tui/src/frontend/render/history.rs
+++ b/crates/trippy-tui/src/frontend/render/history.rs
@@ -1,5 +1,6 @@
 use crate::frontend::render::widgets::sparkline::{EmptyBarSymbol, Sparkline};
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use ratatui::layout::Rect;
 use ratatui::style::Style;
 use ratatui::widgets::{Block, BorderType, Borders};
@@ -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)
diff --git a/crates/trippy-tui/src/frontend/render/settings.rs b/crates/trippy-tui/src/frontend/render/settings.rs
index b307aa7c5..297402e77 100644
--- a/crates/trippy-tui/src/frontend/render/settings.rs
+++ b/crates/trippy-tui/src/frontend/render/settings.rs
@@ -2,6 +2,7 @@ use crate::config::{AddressMode, AsMode, GeoIpMode, IcmpExtensionMode};
 use crate::frontend::render::util;
 use crate::frontend::theme;
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use humantime::format_duration;
 use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
 use ratatui::style::{Modifier, Style};
@@ -30,11 +31,11 @@ pub fn render(f: &mut Frame<'_>, app: &mut TuiApp) {
 
 /// Render settings tabs.
 fn render_settings_tabs(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
-    let titles: Vec<_> = SETTINGS_TABS
-        .iter()
+    let titles: Vec<_> = settings_tabs()
+        .into_iter()
         .map(|(title, _)| {
             Line::from(Span::styled(
-                *title,
+                title,
                 Style::default().fg(app.tui_config.theme.settings_tab_text),
             ))
         })
@@ -42,7 +43,7 @@ fn render_settings_tabs(f: &mut Frame<'_>, app: &TuiApp, rect: Rect) {
     let tabs = Tabs::new(titles)
         .block(
             Block::default()
-                .title(" Settings ")
+                .title(format!(" {} ", t!("title_settings")))
                 .title_alignment(Alignment::Center)
                 .borders(Borders::ALL)
                 .style(Style::default().bg(app.tui_config.theme.settings_dialog_bg))
@@ -62,8 +63,8 @@ fn render_settings_table(
     name: &str,
     items: &[SettingsItem],
 ) {
-    let header_cells = SETTINGS_TABLE_HEADER.iter().map(|h| {
-        Cell::from(*h).style(Style::default().fg(app.tui_config.theme.settings_table_header_text))
+    let header_cells = settings_table_header().into_iter().map(|h| {
+        Cell::from(h).style(Style::default().fg(app.tui_config.theme.settings_table_header_text))
     });
     let header = Row::new(header_cells)
         .style(Style::default().bg(app.tui_config.theme.settings_table_header_bg))
@@ -109,7 +110,7 @@ fn render_settings_info(f: &mut Frame<'_>, app: &TuiApp, rect: Rect, info: &str)
         .wrap(Wrap::default())
         .block(
             Block::default()
-                .title(" Info ")
+                .title(format!(" {} ", t!("settings_info")))
                 .title_alignment(Alignment::Center)
                 .borders(Borders::ALL)
                 .style(Style::default().bg(app.tui_config.theme.settings_dialog_bg))
@@ -120,7 +121,7 @@ fn render_settings_info(f: &mut Frame<'_>, app: &TuiApp, rect: Rect, info: &str)
 }
 
 /// Format all settings.
-fn format_all_settings(app: &TuiApp) -> Vec<(&'static str, String, Vec<SettingsItem>)> {
+fn format_all_settings(app: &TuiApp) -> Vec<(String, String, Vec<SettingsItem>)> {
     let tui_settings = format_tui_settings(app);
     let trace_settings = format_trace_settings(app);
     let dns_settings = format_dns_settings(app);
@@ -133,26 +134,44 @@ fn format_all_settings(app: &TuiApp) -> Vec<(&'static str, String, Vec<SettingsI
     let move_up = app.tui_config.bindings.previous_hop_address.to_string();
     vec![
         (
-            "Tui",
-            String::from("Settings which control how data is displayed in this Tui"),
+            t!("settings_tab_tui_title").to_string(),
+            t!("settings_tab_tui_desc").to_string(),
             tui_settings,
         ),
         (
-            "Trace",
-            String::from("Settings which control the tracing strategy"),
+            t!("settings_tab_trace_title").to_string(),
+            t!("settings_tab_trace_desc").to_string(),
             trace_settings,
         ),
         (
-            "Dns",
-            String::from("Settings which control how DNS lookups are performed"),
+            t!("settings_tab_dns_title").to_string(),
+            t!("settings_tab_dns_desc").to_string(),
             dns_settings,
         ),
-        ("GeoIp", String::from("Settings relating to GeoIp"), geoip_settings),
-        ("Bindings", String::from("Tui key bindings"), bindings_settings),
-        ("Theme", String::from("Tui theme colors"), theme_settings),
         (
-            "Columns",
-            format!("Tui table columns.  Press [{toggle_column}] to toggle a column on or off and use the [{move_down}] and [{move_up}] keys to change the column order."),
+            t!("settings_tab_geoip_title").to_string(),
+            t!("settings_tab_geoip_desc").to_string(),
+            geoip_settings,
+        ),
+        (
+            t!("settings_tab_bindings_title").to_string(),
+            t!("settings_tab_bindings_desc").to_string(),
+            bindings_settings,
+        ),
+        (
+            t!("settings_tab_theme_title").to_string(),
+            t!("settings_tab_theme_desc").to_string(),
+            theme_settings,
+        ),
+        (
+            t!("settings_tab_columns_title").to_string(),
+            t!(
+                "settings_tab_columns_desc",
+                c = toggle_column,
+                d = move_down,
+                u = move_up
+            )
+            .to_string(),
             columns_settings,
         ),
     ]
@@ -190,7 +209,7 @@ fn format_tui_settings(app: &TuiApp) -> Vec<SettingsItem> {
             "tui-max-addrs",
             app.tui_config
                 .max_addrs
-                .map_or_else(|| String::from("auto"), |m| m.to_string()),
+                .map_or_else(|| t!("auto").to_string(), |m| m.to_string()),
         ),
         SettingsItem::new(
             "tui-custom-columns",
@@ -205,12 +224,12 @@ fn format_trace_settings(app: &TuiApp) -> Vec<SettingsItem> {
     let interface = if let Some(iface) = cfg.data.interface() {
         iface.to_string()
     } else {
-        "auto".to_string()
+        t!("auto").to_string()
     };
     let (src_port, dst_port) = match cfg.data.port_direction() {
-        PortDirection::None => ("n/a".to_string(), "n/a".to_string()),
-        PortDirection::FixedDest(dst) => ("auto".to_string(), format!("{}", dst.0)),
-        PortDirection::FixedSrc(src) => (format!("{}", src.0), "auto".to_string()),
+        PortDirection::None => (t!("na").to_string(), t!("na").to_string()),
+        PortDirection::FixedDest(dst) => (t!("auto").to_string(), format!("{}", dst.0)),
+        PortDirection::FixedSrc(src) => (format!("{}", src.0), t!("auto").to_string()),
         PortDirection::FixedBoth(src, dst) => (format!("{}", src.0), format!("{}", dst.0)),
     };
     vec![
@@ -297,8 +316,7 @@ fn format_geoip_settings(app: &TuiApp) -> Vec<SettingsItem> {
         app.tui_config
             .geoip_mmdb_file
             .as_deref()
-            .unwrap_or("none")
-            .to_string(),
+            .map_or_else(|| t!("none").to_string(), ToString::to_string),
     )]
 }
 
@@ -495,21 +513,29 @@ fn format_columns_settings(app: &TuiApp) -> Vec<SettingsItem> {
         .collect()
 }
 
+/// The index of the columns tab.
 pub const SETTINGS_TAB_COLUMNS: usize = 6;
 
 /// The name and number of items for each tabs in the setting dialog.
-pub const SETTINGS_TABS: [(&str, usize); 7] = [
-    ("Tui", 9),
-    ("Trace", 17),
-    ("Dns", 5),
-    ("GeoIp", 1),
-    ("Bindings", 36),
-    ("Theme", 31),
-    ("Columns", 0),
-];
+pub fn settings_tabs() -> [(String, usize); 7] {
+    [
+        (t!("settings_tab_tui_title").to_string(), 9),
+        (t!("settings_tab_trace_title").to_string(), 17),
+        (t!("settings_tab_dns_title").to_string(), 5),
+        (t!("settings_tab_geoip_title").to_string(), 1),
+        (t!("settings_tab_bindings_title").to_string(), 36),
+        (t!("settings_tab_theme_title").to_string(), 31),
+        (t!("settings_tab_columns_title").to_string(), 0),
+    ]
+}
 
 /// The settings table header.
-const SETTINGS_TABLE_HEADER: [&str; 2] = ["Setting", "Value"];
+pub fn settings_table_header() -> [String; 2] {
+    [
+        t!("settings_table_header_setting").to_string(),
+        t!("settings_table_header_value").to_string(),
+    ]
+}
 
 const SETTINGS_TABLE_WIDTH: [Constraint; 3] = [
     Constraint::Length(3),
diff --git a/crates/trippy-tui/src/frontend/render/splash.rs b/crates/trippy-tui/src/frontend/render/splash.rs
index f9315fb01..c7ce0eef4 100644
--- a/crates/trippy-tui/src/frontend/render/splash.rs
+++ b/crates/trippy-tui/src/frontend/render/splash.rs
@@ -1,9 +1,11 @@
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use ratatui::layout::{Alignment, Constraint, Layout, Rect};
 use ratatui::style::Style;
 use ratatui::text::{Line, Span};
 use ratatui::widgets::{Block, BorderType, Borders, Paragraph};
 use ratatui::Frame;
+use std::borrow::Cow;
 
 /// Render the splash screen.
 ///
@@ -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(Line::raw(t!("title_hops")))
         .borders(Borders::ALL)
         .border_type(BorderType::Rounded)
         .border_style(Style::default().fg(app.tui_config.theme.border))
@@ -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()
diff --git a/crates/trippy-tui/src/frontend/render/table.rs b/crates/trippy-tui/src/frontend/render/table.rs
index 83daa106c..bcd24d776 100644
--- a/crates/trippy-tui/src/frontend/render/table.rs
+++ b/crates/trippy-tui/src/frontend/render/table.rs
@@ -4,8 +4,10 @@ use crate::frontend::config::TuiConfig;
 use crate::frontend::theme::Theme;
 use crate::frontend::tui_app::TuiApp;
 use crate::geoip::{GeoIpCity, GeoIpLookup};
+use crate::t;
 use itertools::Itertools;
 use ratatui::layout::Rect;
+use ratatui::prelude::Line;
 use ratatui::style::{Modifier, Style};
 use ratatui::widgets::{Block, BorderType, Borders, Cell, Row, Table};
 use ratatui::Frame;
@@ -63,7 +65,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(Line::raw(t!("title_hops"))),
         )
         .style(
             Style::default()
@@ -178,9 +180,9 @@ fn render_usize_cell(value: usize) -> Cell<'static> {
 
 fn render_nat_cell(value: NatStatus) -> Cell<'static> {
     Cell::from(match value {
-        NatStatus::NotApplicable => "n/a",
-        NatStatus::NotDetected => "No",
-        NatStatus::Detected => "Yes",
+        NatStatus::NotApplicable => t!("na"),
+        NatStatus::NotDetected => t!("no"),
+        NatStatus::Detected => t!("yes"),
     })
 }
 
@@ -240,7 +242,7 @@ fn render_icmp_packet_code_cell(icmp_packet_type: Option<IcmpPacketType>) -> Cel
             | IcmpPacketType::TimeExceeded(code)
             | IcmpPacketType::EchoReply(code),
         ) => Cell::from(format!("{}", code.0)),
-        _ => Cell::from("n/a"),
+        _ => Cell::from(t!("na")),
     }
 }
 
@@ -248,7 +250,7 @@ fn render_port_cell(port: u16) -> Cell<'static> {
     if port > 0 {
         Cell::from(format!("{port}"))
     } else {
-        Cell::from("n/a")
+        Cell::from(t!("na"))
     }
 }
 
@@ -261,7 +263,7 @@ fn render_hostname(
 ) -> (Cell<'static>, u16) {
     let (hostname, count) = if hop.total_recv() > 0 {
         if app.hide_private_hops && app.tui_config.privacy_max_ttl >= hop.ttl() {
-            (String::from("**Hidden**"), 1)
+            (format!("**{}**", t!("hidden")), 1)
         } else {
             match app.tui_config.max_addrs {
                 None => {
@@ -290,7 +292,7 @@ fn render_hostname(
             }
         }
     } else {
-        (String::from("No response"), 1)
+        (format!("{}", t!("no_response")), 1)
     };
     (Cell::from(hostname), count)
 }
@@ -389,8 +391,8 @@ fn format_dns_entry(dns_entry: DnsEntry, lookup_as_info: bool, as_mode: AsMode)
                 format!("{ip}")
             }
         }
-        DnsEntry::Failed(ip) => format!("Failed: {ip}"),
-        DnsEntry::Timeout(ip) => format!("Timeout: {ip}"),
+        DnsEntry::Failed(ip) => format!("{}: {ip}", t!("dns_failed")),
+        DnsEntry::Timeout(ip) => format!("{}: {ip}", t!("dns_timeout")),
     }
 }
 
@@ -438,7 +440,7 @@ fn format_extensions_mpls(extensions: &Extensions) -> Option<String> {
     if labels.is_empty() {
         None
     } else {
-        Some(format!("labels: {labels}"))
+        Some(format!("{}: {labels}", t!("labels")))
     }
 }
 
@@ -511,13 +513,13 @@ fn render_hostname_with_details(
 ) -> (Cell<'static>, u16) {
     let rendered = if hop.total_recv() > 0 {
         if app.hide_private_hops && config.privacy_max_ttl >= hop.ttl() {
-            String::from("**Hidden**")
+            format!("**{}**", t!("hidden"))
         } else {
             let index = app.selected_hop_address;
             format_details(hop, index, dns, geoip_lookup, config)
         }
     } else {
-        String::from("No response")
+        format!("{}", t!("no_response"))
     };
     (Cell::from(rendered), 7)
 }
@@ -592,10 +594,10 @@ fn format_details(
             config,
         ),
         DnsEntry::Failed(ip) => {
-            format!("Failed: {ip}")
+            format!("{}: {ip}", t!("dns_failed"))
         }
         DnsEntry::Timeout(ip) => {
-            format!("Timeout: {ip}")
+            format!("{}: {ip}", t!("dns_timeout"))
         }
     }
 }
@@ -613,7 +615,7 @@ fn format_details(
 /// Pos: 37.751, -97.822 (~1000km)
 /// Ext: [mpls(label=48268, ttl=1, exp=0, bos=1)]
 /// ```
-#[allow(clippy::too_many_arguments)]
+#[allow(clippy::too_many_arguments, clippy::cognitive_complexity)]
 fn fmt_details_line(
     addr: IpAddr,
     index: usize,
@@ -626,41 +628,74 @@ fn fmt_details_line(
     config: &TuiConfig,
 ) -> String {
     let as_fmt = match (config.lookup_as_info, asinfo) {
-        (false, _) => "AS Name: <not enabled>\nAS Info: <not enabled>".to_string(),
-        (true, None) => "AS Name: <awaited>\nAS Info: <awaited>".to_string(),
+        (false, _) => format!(
+            "AS {}: <{}>\nAS {}: <{}>",
+            t!("name"),
+            t!("info"),
+            t!("not_enabled"),
+            t!("not_enabled")
+        ),
+        (true, None) => format!(
+            "AS {}: <{}>\nAS {}: <{}>",
+            t!("name"),
+            t!("info"),
+            t!("awaited"),
+            t!("awaited")
+        ),
         (true, Some(info)) if info.asn.is_empty() => {
-            "AS Name: <not found>\nAS Info: <not found>".to_string()
+            format!(
+                "AS {}: <{}>\nAS {}: <{}>",
+                t!("name"),
+                t!("info"),
+                t!("not_found"),
+                t!("not_found")
+            )
         }
         (true, Some(info)) => format!(
-            "AS Name: AS{} {}\nAS Info: {} {} {}",
-            info.asn, info.name, info.prefix, info.registry, info.allocated
+            "AS {}: AS{} {}\nAS {}: {} {} {}",
+            t!("name"),
+            t!("info"),
+            info.asn,
+            info.name,
+            info.prefix,
+            info.registry,
+            info.allocated
         ),
     };
     let hosts_rendered = if let Some(hosts) = hostnames {
         if hosts.is_empty() {
-            "Host: <not found>".to_string()
+            format!("{}: <{}>", t!("host"), t!("not_found"))
         } else {
-            format!("Host: {}", hosts.join(" "))
+            format!("{}: {}", t!("host"), hosts.join(" "))
         }
     } else {
-        "Host: <awaited>".to_string()
+        format!("{}: <{}>", t!("host"), t!("awaited"))
     };
     let geoip_fmt = if let Some(geo) = geoip {
         let (lat, long, radius) = geo.coordinates().unwrap_or_default();
         format!(
-            "Geo: {}\nPos: {}, {} (~{}km)",
+            "{}: {}\n{}: {}, {} (~{}{})",
+            t!("geo"),
             geo.long_name(),
+            t!("pos"),
             lat,
             long,
-            radius
+            radius,
+            t!("kilometer"),
         )
     } else {
-        "Geo: <not found>\nPos: <not found>".to_string()
+        format!(
+            "{}: <{}>\n{}: <{}>",
+            t!("geo"),
+            t!("not_found"),
+            t!("pos"),
+            t!("not_found")
+        )
     };
     let ext_fmt = if let Some(extensions) = extensions {
-        format!("Ext: [{}]", format_extensions_all(extensions))
+        format!("{}: [{}]", t!("geo"), format_extensions_all(extensions))
     } else {
-        "Ext: <none>".to_string()
+        format!("{}: <{}>", t!("geo"), t!("none"))
     };
     let nat_fmt = match nat {
         NatStatus::Detected => " [NAT]",
diff --git a/crates/trippy-tui/src/frontend/render/tabs.rs b/crates/trippy-tui/src/frontend/render/tabs.rs
index 8e15941bc..2f802311d 100644
--- a/crates/trippy-tui/src/frontend/render/tabs.rs
+++ b/crates/trippy-tui/src/frontend/render/tabs.rs
@@ -1,4 +1,5 @@
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use ratatui::layout::{Alignment, Rect};
 use ratatui::style::{Modifier, Style};
 use ratatui::text::{Line, Span};
@@ -8,7 +9,7 @@ use ratatui::Frame;
 /// Render the tabs, one per trace.
 pub fn render(f: &mut Frame<'_>, rect: Rect, app: &TuiApp) {
     let tabs_block = Block::default()
-        .title("Traces")
+        .title(Line::raw(t!("title_traces")))
         .title_alignment(Alignment::Left)
         .borders(Borders::ALL)
         .border_type(BorderType::Rounded)
diff --git a/crates/trippy-tui/src/frontend/render/world.rs b/crates/trippy-tui/src/frontend/render/world.rs
index 304e8315a..4b23df9d5 100644
--- a/crates/trippy-tui/src/frontend/render/world.rs
+++ b/crates/trippy-tui/src/frontend/render/world.rs
@@ -1,6 +1,8 @@
 use crate::frontend::tui_app::TuiApp;
+use crate::t;
 use itertools::Itertools;
 use ratatui::layout::{Alignment, Constraint, Direction, Layout, Margin, Rect};
+use ratatui::prelude::Line;
 use ratatui::style::{Color, Style};
 use ratatui::symbols::Marker;
 use ratatui::text::Span;
@@ -32,7 +34,7 @@ fn render_map_canvas(f: &mut Frame<'_>, app: &TuiApp, rect: Rect, entries: &[Map
         .background_color(app.tui_config.theme.bg)
         .block(
             Block::default()
-                .title("Map")
+                .title(Line::raw(t!("title_map")))
                 .borders(Borders::ALL)
                 .border_style(Style::default().fg(app.tui_config.theme.border))
                 .style(
@@ -145,24 +147,29 @@ fn render_map_info_panel(f: &mut Frame<'_>, app: &TuiApp, rect: Rect, entries: &
         })
         .collect::<Vec<_>>();
     let info = if app.hide_private_hops && app.tui_config.privacy_max_ttl >= selected_hop.ttl() {
-        "**Hidden**".to_string()
+        format!("**{}**", t!("hidden"))
     } else {
         match locations.as_slice() {
-            _ if app.tui_config.geoip_mmdb_file.is_none() => "GeoIp not enabled".to_string(),
+            _ if app.tui_config.geoip_mmdb_file.is_none() => t!("geoip_not_enabled").to_string(),
             [] if selected_hop.addr_count() > 0 => format!(
-                "No GeoIp data for hop {} ({})",
+                "{} {} ({})",
+                t!("geoip_no_data_for_hop"),
                 selected_hop.ttl(),
                 selected_hop.addrs().join(", ")
             ),
-            [] => format!("No GeoIp data for hop {}", selected_hop.ttl()),
+            [] => format!("{} {}", t!("geoip_no_data_for_hop"), selected_hop.ttl()),
             [loc] => loc.to_string(),
-            _ => format!("Multiple GeoIp locations for hop {}", selected_hop.ttl()),
+            _ => format!(
+                "{} {}",
+                t!("geoip_multiple_data_for_hop"),
+                selected_hop.ttl()
+            ),
         }
     };
     let info_panel = Paragraph::new(info)
         .block(
             Block::default()
-                .title(format!("Hop {}", selected_hop.ttl()))
+                .title(format!("{} {}", t!("hop"), selected_hop.ttl()))
                 .borders(Borders::ALL)
                 .border_type(BorderType::Rounded)
                 .border_style(Style::default().fg(theme.map_info_panel_border))
@@ -198,7 +205,7 @@ fn build_map_entries(app: &TuiApp) -> Vec<MapEntry> {
                 if let Some((latitude, longitude, radius)) = geo.coordinates() {
                     let entry = geo_map.entry(geo.long_name()).or_insert(MapEntry {
                         long_name: geo.long_name(),
-                        location: format!("{latitude}, {longitude} ~{radius}km"),
+                        location: format!("{latitude}, {longitude} ~{radius}{}", t!("kilometer")),
                         latitude,
                         longitude,
                         radius,
diff --git a/crates/trippy-tui/src/frontend/tui_app.rs b/crates/trippy-tui/src/frontend/tui_app.rs
index 72e1a2706..17b59afc3 100644
--- a/crates/trippy-tui/src/frontend/tui_app.rs
+++ b/crates/trippy-tui/src/frontend/tui_app.rs
@@ -1,6 +1,6 @@
 use crate::app::TraceInfo;
 use crate::frontend::config::TuiConfig;
-use crate::frontend::render::settings::{SETTINGS_TABS, SETTINGS_TAB_COLUMNS};
+use crate::frontend::render::settings::{settings_tabs, SETTINGS_TAB_COLUMNS};
 use crate::geoip::GeoIpLookup;
 use itertools::Itertools;
 use ratatui::widgets::TableState;
@@ -238,7 +238,7 @@ impl TuiApp {
     }
 
     pub fn next_settings_tab(&mut self) {
-        if self.settings_tab_selected < SETTINGS_TABS.len() - 1 {
+        if self.settings_tab_selected < settings_tabs().len() - 1 {
             self.settings_tab_selected += 1;
         }
         self.setting_table_state.select(Some(0));
@@ -286,7 +286,7 @@ impl TuiApp {
         if self.settings_tab_selected == SETTINGS_TAB_COLUMNS {
             self.tui_config.tui_columns.all_columns_count()
         } else {
-            SETTINGS_TABS[self.settings_tab_selected].1
+            settings_tabs()[self.settings_tab_selected].1
         }
     }
 
diff --git a/crates/trippy-tui/src/lib.rs b/crates/trippy-tui/src/lib.rs
index d14757963..c381c3975 100644
--- a/crates/trippy-tui/src/lib.rs
+++ b/crates/trippy-tui/src/lib.rs
@@ -15,10 +15,14 @@ mod app;
 mod config;
 mod frontend;
 mod geoip;
+mod locale;
 mod print;
 mod report;
 mod util;
 
+// initialize the i18n system.
+rust_i18n::i18n!("locales", fallback = "en");
+
 /// Run the Trippy application.
 pub fn trippy() -> anyhow::Result<()> {
     let args = Args::parse();
diff --git a/crates/trippy-tui/src/locale.rs b/crates/trippy-tui/src/locale.rs
new file mode 100644
index 000000000..c3fee3f9b
--- /dev/null
+++ b/crates/trippy-tui/src/locale.rs
@@ -0,0 +1,70 @@
+const FALLBACK_LOCALE: &str = "en";
+
+/// Set the locale for the application.
+///
+/// If the given locale is `None` or unsupported, the system locale is tried. If the system locale
+/// is not supported, the fallback locale is used.
+///
+/// In both cases, the language part of the locale is used if the full locale is not supported.
+pub fn set_locale(locale: Option<&str>) {
+    if let Some(locale) = locale {
+        set_locale_inner(locale);
+    } else if let Some(locale) = sys_locale::get_locale().as_ref() {
+        set_locale_inner(locale);
+    } else {
+        set_locale_inner(FALLBACK_LOCALE);
+    }
+}
+
+fn set_locale_inner(locale: &str) {
+    let all_locales = rust_i18n::available_locales!();
+    if all_locales.contains(&locale) {
+        rust_i18n::set_locale(locale);
+    } else {
+        let language = split_locale(locale);
+        if all_locales.contains(&language.as_str()) {
+            rust_i18n::set_locale(&language);
+        } else {
+            rust_i18n::set_locale(FALLBACK_LOCALE);
+        }
+    }
+}
+
+fn split_locale(locale: &str) -> String {
+    let mut parts = locale.split(['-', '_']);
+    parts
+        .next()
+        .map_or_else(|| FALLBACK_LOCALE, |lang| lang)
+        .to_string()
+}
+
+// A macro for translating a text string.
+#[macro_export]
+macro_rules! t {
+    ($($all:tt)*) => {
+        rust_i18n::t!($($all)*)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_split_locale_dash() {
+        let language = split_locale("en-US");
+        assert_eq!(language, "en");
+    }
+
+    #[test]
+    fn test_split_locale_underscore() {
+        let language = split_locale("en_US");
+        assert_eq!(language, "en");
+    }
+
+    #[test]
+    fn test_split_locale_no_region() {
+        let language = split_locale("en");
+        assert_eq!(language, "en");
+    }
+}
diff --git a/trippy-config-sample.toml b/trippy-config-sample.toml
index bda6d3475..be78fdbe2 100644
--- a/trippy-config-sample.toml
+++ b/trippy-config-sample.toml
@@ -327,6 +327,8 @@ tui-refresh-rate = "100ms"
 # The maximum ttl of hops which will be masked for privacy [default: 1]
 tui-privacy-max-ttl = 0
 
+# The locale to use for Tui [default: auto]
+#tui-locale = "en-US"
 
 # Tui color theme configure.
 #


Test patch:
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip.snap
index 64e52c1be..385b21d9c 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/config.rs
 ---
-AnetworkdiagnostictoolUsage:trip[OPTIONS][TARGETS]...Arguments:[TARGETS]...AspacedelimitedlistofhostnamesandIPstotraceOptions:-c,--config-file<CONFIG_FILE>Configfile-m,--mode<MODE>Outputmode[default:tui][possiblevalues:tui,stream,pretty,markdown,csv,json,dot,flows,silent]-u,--unprivilegedTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]-p,--protocol<PROTOCOL>Tracingprotocol[default:icmp][possiblevalues:icmp,udp,tcp]--udpTraceusingtheUDPprotocol--tcpTraceusingtheTCPprotocol--icmpTraceusingtheICMPprotocol-F,--addr-family<ADDR_FAMILY>Theaddressfamily[default:Ipv4thenIpv6][possiblevalues:ipv4,ipv6,ipv6-then-ipv4,ipv4-then-ipv6]-4,--ipv4UseIPv4only-6,--ipv6UseIPv6only-P,--target-port<TARGET_PORT>Thetargetport(TCP&UDPonly)[default:80]-S,--source-port<SOURCE_PORT>Thesourceport(TCP&UDPonly)[default:auto]-A,--source-address<SOURCE_ADDRESS>ThesourceIPaddress[default:auto]-I,--interface<INTERFACE>Thenetworkinterface[default:auto]-i,--min-round-duration<MIN_ROUND_DURATION>Theminimumdurationofeveryround[default:1s]-T,--max-round-duration<MAX_ROUND_DURATION>Themaximumdurationofeveryround[default:1s]-g,--grace-duration<GRACE_DURATION>TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]--initial-sequence<INITIAL_SEQUENCE>Theinitialsequencenumber[default:33434]-R,--multipath-strategy<MULTIPATH_STRATEGY>TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic][possiblevalues:classic,paris,dublin]-U,--max-inflight<MAX_INFLIGHT>Themaximumnumberofin-flightICMPechorequests[default:24]-f,--first-ttl<FIRST_TTL>TheTTLtostartfrom[default:1]-t,--max-ttl<MAX_TTL>ThemaximumnumberofTTLhops[default:64]--packet-size<PACKET_SIZE>ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]--payload-pattern<PAYLOAD_PATTERN>TherepeatingpatterninthepayloadoftheICMPpacket[default:0]-Q,--tos<TOS>TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]-e,--icmp-extensionsParseICMPextensions--read-timeout<READ_TIMEOUT>Thesocketreadtimeout[default:10ms]-r,--dns-resolve-method<DNS_RESOLVE_METHOD>HowtoperformDNSqueries[default:system][possiblevalues:system,resolv,google,cloudflare]-y,--dns-resolve-allTracetoallIPsresolvedfromDNSlookup[default:false]--dns-timeout<DNS_TIMEOUT>ThemaximumtimetowaittoperformDNSqueries[default:5s]--dns-ttl<DNS_TTL>Thetime-to-live(TTL)ofDNSentries[default:300s]-z,--dns-lookup-as-infoLookupautonomoussystem(AS)informationduringDNSqueries[default:false]-s,--max-samples<MAX_SAMPLES>Themaximumnumberofsamplestorecordperhop[default:256]--max-flows<MAX_FLOWS>Themaximumnumberofflowstorecord[default:64]-a,--tui-address-mode<TUI_ADDRESS_MODE>Howtorenderaddresses[default:host][possiblevalues:ip,host,both]--tui-as-mode<TUI_AS_MODE>HowtorenderASinformation[default:asn][possiblevalues:asn,prefix,country-code,registry,allocated,name]--tui-custom-columns<TUI_CUSTOM_COLUMNS>CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]--tui-icmp-extension-mode<TUI_ICMP_EXTENSION_MODE>HowtorenderICMPextensions[default:off][possiblevalues:off,mpls,full,all]--tui-geoip-mode<TUI_GEOIP_MODE>HowtorenderGeoIpinformation[default:short][possiblevalues:off,short,long,location]-M,--tui-max-addrs<TUI_MAX_ADDRS>Themaximumnumberofaddressestoshowperhop[default:auto]--tui-preserve-screenPreservethescreenonexit[default:false]--tui-refresh-rate<TUI_REFRESH_RATE>TheTuirefreshrate[default:100ms]--tui-privacy-max-ttl<TUI_PRIVACY_MAX_TTL>Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]--tui-theme-colors<TUI_THEME_COLORS>TheTUIthemecolors[item=color,item=color,..]--print-tui-theme-itemsPrintallTUIthemeitemsandexit--tui-key-bindings<TUI_KEY_BINDINGS>TheTUIkeybindings[command=key,command=key,..]--print-tui-binding-commandsPrintallTUIcommandsthatcanbeboundandexit-C,--report-cycles<REPORT_CYCLES>Thenumberofreportcyclestorun[default:10]-G,--geoip-mmdb-file<GEOIP_MMDB_FILE>ThesupportedMaxMindorIPinfoGeoIpmmdbfile--generate<GENERATE>Generateshellcompletion[possiblevalues:bash,elvish,fish,powershell,zsh]--generate-manGenerateROFFmanpage--print-config-templatePrintatemplatetomlconfigfileandexit--log-format<LOG_FORMAT>Thedebuglogformat[default:pretty][possiblevalues:compact,pretty,json,chrome]--log-filter<LOG_FILTER>Thedebuglogfilter[default:trippy=debug]--log-span-events<LOG_SPAN_EVENTS>Thedebuglogformat[default:off][possiblevalues:off,active,full]-v,--verboseEnableverbosedebuglogging-h,--helpPrinthelp(seemorewith'--help')-V,--versionPrintversion
+AnetworkdiagnostictoolUsage:trip[OPTIONS][TARGETS]...Arguments:[TARGETS]...AspacedelimitedlistofhostnamesandIPstotraceOptions:-c,--config-file<CONFIG_FILE>Configfile-m,--mode<MODE>Outputmode[default:tui][possiblevalues:tui,stream,pretty,markdown,csv,json,dot,flows,silent]-u,--unprivilegedTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]-p,--protocol<PROTOCOL>Tracingprotocol[default:icmp][possiblevalues:icmp,udp,tcp]--udpTraceusingtheUDPprotocol--tcpTraceusingtheTCPprotocol--icmpTraceusingtheICMPprotocol-F,--addr-family<ADDR_FAMILY>Theaddressfamily[default:Ipv4thenIpv6][possiblevalues:ipv4,ipv6,ipv6-then-ipv4,ipv4-then-ipv6]-4,--ipv4UseIPv4only-6,--ipv6UseIPv6only-P,--target-port<TARGET_PORT>Thetargetport(TCP&UDPonly)[default:80]-S,--source-port<SOURCE_PORT>Thesourceport(TCP&UDPonly)[default:auto]-A,--source-address<SOURCE_ADDRESS>ThesourceIPaddress[default:auto]-I,--interface<INTERFACE>Thenetworkinterface[default:auto]-i,--min-round-duration<MIN_ROUND_DURATION>Theminimumdurationofeveryround[default:1s]-T,--max-round-duration<MAX_ROUND_DURATION>Themaximumdurationofeveryround[default:1s]-g,--grace-duration<GRACE_DURATION>TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]--initial-sequence<INITIAL_SEQUENCE>Theinitialsequencenumber[default:33434]-R,--multipath-strategy<MULTIPATH_STRATEGY>TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic][possiblevalues:classic,paris,dublin]-U,--max-inflight<MAX_INFLIGHT>Themaximumnumberofin-flightICMPechorequests[default:24]-f,--first-ttl<FIRST_TTL>TheTTLtostartfrom[default:1]-t,--max-ttl<MAX_TTL>ThemaximumnumberofTTLhops[default:64]--packet-size<PACKET_SIZE>ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]--payload-pattern<PAYLOAD_PATTERN>TherepeatingpatterninthepayloadoftheICMPpacket[default:0]-Q,--tos<TOS>TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]-e,--icmp-extensionsParseICMPextensions--read-timeout<READ_TIMEOUT>Thesocketreadtimeout[default:10ms]-r,--dns-resolve-method<DNS_RESOLVE_METHOD>HowtoperformDNSqueries[default:system][possiblevalues:system,resolv,google,cloudflare]-y,--dns-resolve-allTracetoallIPsresolvedfromDNSlookup[default:false]--dns-timeout<DNS_TIMEOUT>ThemaximumtimetowaittoperformDNSqueries[default:5s]--dns-ttl<DNS_TTL>Thetime-to-live(TTL)ofDNSentries[default:300s]-z,--dns-lookup-as-infoLookupautonomoussystem(AS)informationduringDNSqueries[default:false]-s,--max-samples<MAX_SAMPLES>Themaximumnumberofsamplestorecordperhop[default:256]--max-flows<MAX_FLOWS>Themaximumnumberofflowstorecord[default:64]-a,--tui-address-mode<TUI_ADDRESS_MODE>Howtorenderaddresses[default:host][possiblevalues:ip,host,both]--tui-as-mode<TUI_AS_MODE>HowtorenderASinformation[default:asn][possiblevalues:asn,prefix,country-code,registry,allocated,name]--tui-custom-columns<TUI_CUSTOM_COLUMNS>CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]--tui-icmp-extension-mode<TUI_ICMP_EXTENSION_MODE>HowtorenderICMPextensions[default:off][possiblevalues:off,mpls,full,all]--tui-geoip-mode<TUI_GEOIP_MODE>HowtorenderGeoIpinformation[default:short][possiblevalues:off,short,long,location]-M,--tui-max-addrs<TUI_MAX_ADDRS>Themaximumnumberofaddressestoshowperhop[default:auto]--tui-preserve-screenPreservethescreenonexit[default:false]--tui-refresh-rate<TUI_REFRESH_RATE>TheTuirefreshrate[default:100ms]--tui-privacy-max-ttl<TUI_PRIVACY_MAX_TTL>Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]--tui-locale<TUI_LOCALE>ThelocaletousefortheTUI[default:auto]--tui-theme-colors<TUI_THEME_COLORS>TheTUIthemecolors[item=color,item=color,..]--print-tui-theme-itemsPrintallTUIthemeitemsandexit--tui-key-bindings<TUI_KEY_BINDINGS>TheTUIkeybindings[command=key,command=key,..]--print-tui-binding-commandsPrintallTUIcommandsthatcanbeboundandexit-C,--report-cycles<REPORT_CYCLES>Thenumberofreportcyclestorun[default:10]-G,--geoip-mmdb-file<GEOIP_MMDB_FILE>ThesupportedMaxMindorIPinfoGeoIpmmdbfile--generate<GENERATE>Generateshellcompletion[possiblevalues:bash,elvish,fish,powershell,zsh]--generate-manGenerateROFFmanpage--print-config-templatePrintatemplatetomlconfigfileandexit--log-format<LOG_FORMAT>Thedebuglogformat[default:pretty][possiblevalues:compact,pretty,json,chrome]--log-filter<LOG_FILTER>Thedebuglogfilter[default:trippy=debug]--log-span-events<LOG_SPAN_EVENTS>Thedebuglogformat[default:off][possiblevalues:off,active,full]-v,--verboseEnableverbosedebuglogging-h,--helpPrinthelp(seemorewith'--help')-V,--versionPrintversion
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip_--help.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip_--help.snap
index 34d09727c..139240092 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip_--help.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip_--help.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/config.rs
 ---
-AnetworkdiagnostictoolUsage:trip[OPTIONS][TARGETS]...Arguments:[TARGETS]...AspacedelimitedlistofhostnamesandIPstotraceOptions:-c,--config-file<CONFIG_FILE>Configfile-m,--mode<MODE>Outputmode[default:tui]Possiblevalues:-tui:DisplayinteractiveTUI-stream:Displayacontinuousstreamoftracingdata-pretty:GenerateaprettytexttablereportforNcycles-markdown:GenerateaMarkdowntexttablereportforNcycles-csv:GenerateaCSVreportforNcycles-json:GenerateaJSONreportforNcycles-dot:GenerateaGraphvizDOTfileforNcycles-flows:DisplayallflowsforNcycles-silent:DonotgenerateanytracingoutputforNcycles-u,--unprivilegedTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]-p,--protocol<PROTOCOL>Tracingprotocol[default:icmp]Possiblevalues:-icmp:InternetControlMessageProtocol-udp:UserDatagramProtocol-tcp:TransmissionControlProtocol--udpTraceusingtheUDPprotocol--tcpTraceusingtheTCPprotocol--icmpTraceusingtheICMPprotocol-F,--addr-family<ADDR_FAMILY>Theaddressfamily[default:Ipv4thenIpv6]Possiblevalues:-ipv4:Ipv4only-ipv6:Ipv6only-ipv6-then-ipv4:Ipv6withafallbacktoIpv4-ipv4-then-ipv6:Ipv4withafallbacktoIpv6-4,--ipv4UseIPv4only-6,--ipv6UseIPv6only-P,--target-port<TARGET_PORT>Thetargetport(TCP&UDPonly)[default:80]-S,--source-port<SOURCE_PORT>Thesourceport(TCP&UDPonly)[default:auto]-A,--source-address<SOURCE_ADDRESS>ThesourceIPaddress[default:auto]-I,--interface<INTERFACE>Thenetworkinterface[default:auto]-i,--min-round-duration<MIN_ROUND_DURATION>Theminimumdurationofeveryround[default:1s]-T,--max-round-duration<MAX_ROUND_DURATION>Themaximumdurationofeveryround[default:1s]-g,--grace-duration<GRACE_DURATION>TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]--initial-sequence<INITIAL_SEQUENCE>Theinitialsequencenumber[default:33434]-R,--multipath-strategy<MULTIPATH_STRATEGY>TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]Possiblevalues:-classic:Thesrcordestportisusedtostorethesequencenumber-paris:TheUDP`checksum`fieldisusedtostorethesequencenumber-dublin:TheIP`identifier`fieldisusedtostorethesequencenumber-U,--max-inflight<MAX_INFLIGHT>Themaximumnumberofin-flightICMPechorequests[default:24]-f,--first-ttl<FIRST_TTL>TheTTLtostartfrom[default:1]-t,--max-ttl<MAX_TTL>ThemaximumnumberofTTLhops[default:64]--packet-size<PACKET_SIZE>ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]--payload-pattern<PAYLOAD_PATTERN>TherepeatingpatterninthepayloadoftheICMPpacket[default:0]-Q,--tos<TOS>TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]-e,--icmp-extensionsParseICMPextensions--read-timeout<READ_TIMEOUT>Thesocketreadtimeout[default:10ms]-r,--dns-resolve-method<DNS_RESOLVE_METHOD>HowtoperformDNSqueries[default:system]Possiblevalues:-system:ResolveusingtheOSresolver-resolv:Resolveusingthe`/etc/resolv.conf`DNSconfiguration-google:ResolveusingtheGoogle`8.8.8.8`DNSservice-cloudflare:ResolveusingtheCloudflare`1.1.1.1`DNSservice-y,--dns-resolve-allTracetoallIPsresolvedfromDNSlookup[default:false]--dns-timeout<DNS_TIMEOUT>ThemaximumtimetowaittoperformDNSqueries[default:5s]--dns-ttl<DNS_TTL>Thetime-to-live(TTL)ofDNSentries[default:300s]-z,--dns-lookup-as-infoLookupautonomoussystem(AS)informationduringDNSqueries[default:false]-s,--max-samples<MAX_SAMPLES>Themaximumnumberofsamplestorecordperhop[default:256]--max-flows<MAX_FLOWS>Themaximumnumberofflowstorecord[default:64]-a,--tui-address-mode<TUI_ADDRESS_MODE>Howtorenderaddresses[default:host]Possiblevalues:-ip:ShowIPaddressonly-host:Showreverse-lookupDNShostnameonly-both:ShowbothIPaddressandreverse-lookupDNShostname--tui-as-mode<TUI_AS_MODE>HowtorenderASinformation[default:asn]Possiblevalues:-asn:ShowtheASN-prefix:DisplaytheASprefix-country-code:Displaythecountrycode-registry:Displaytheregistryname-allocated:Displaytheallocateddate-name:DisplaytheASname--tui-custom-columns<TUI_CUSTOM_COLUMNS>CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]--tui-icmp-extension-mode<TUI_ICMP_EXTENSION_MODE>HowtorenderICMPextensions[default:off]Possiblevalues:-off:Donotshow`icmp`extensions-mpls:ShowMPLSlabel(s)only-full:Showfull`icmp`extensiondataforallknownextensions-all:Showfull`icmp`extensiondataforallclasses--tui-geoip-mode<TUI_GEOIP_MODE>HowtorenderGeoIpinformation[default:short]Possiblevalues:-off:DonotdisplayGeoIpdata-short:Showshortformat-long:Showlongformat-location:ShowlatitudeandLongitudeformat-M,--tui-max-addrs<TUI_MAX_ADDRS>Themaximumnumberofaddressestoshowperhop[default:auto]--tui-preserve-screenPreservethescreenonexit[default:false]--tui-refresh-rate<TUI_REFRESH_RATE>TheTuirefreshrate[default:100ms]--tui-privacy-max-ttl<TUI_PRIVACY_MAX_TTL>Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]--tui-theme-colors<TUI_THEME_COLORS>TheTUIthemecolors[item=color,item=color,..]--print-tui-theme-itemsPrintallTUIthemeitemsandexit--tui-key-bindings<TUI_KEY_BINDINGS>TheTUIkeybindings[command=key,command=key,..]--print-tui-binding-commandsPrintallTUIcommandsthatcanbeboundandexit-C,--report-cycles<REPORT_CYCLES>Thenumberofreportcyclestorun[default:10]-G,--geoip-mmdb-file<GEOIP_MMDB_FILE>ThesupportedMaxMindorIPinfoGeoIpmmdbfile--generate<GENERATE>Generateshellcompletion[possiblevalues:bash,elvish,fish,powershell,zsh]--generate-manGenerateROFFmanpage--print-config-templatePrintatemplatetomlconfigfileandexit--log-format<LOG_FORMAT>Thedebuglogformat[default:pretty]Possiblevalues:-compact:Displaylogdatainacompactformat-pretty:Displaylogdatainaprettyformat-json:Displaylogdatainajsonformat-chrome:DisplaylogdatainChrometraceformat--log-filter<LOG_FILTER>Thedebuglogfilter[default:trippy=debug]--log-span-events<LOG_SPAN_EVENTS>Thedebuglogformat[default:off]Possiblevalues:-off:Donotdisplayeventspans-active:Displayenterandexiteventspans-full:Displayalleventspans-v,--verboseEnableverbosedebuglogging-h,--helpPrinthelp(seeasummarywith'-h')-V,--versionPrintversion
+AnetworkdiagnostictoolUsage:trip[OPTIONS][TARGETS]...Arguments:[TARGETS]...AspacedelimitedlistofhostnamesandIPstotraceOptions:-c,--config-file<CONFIG_FILE>Configfile-m,--mode<MODE>Outputmode[default:tui]Possiblevalues:-tui:DisplayinteractiveTUI-stream:Displayacontinuousstreamoftracingdata-pretty:GenerateaprettytexttablereportforNcycles-markdown:GenerateaMarkdowntexttablereportforNcycles-csv:GenerateaCSVreportforNcycles-json:GenerateaJSONreportforNcycles-dot:GenerateaGraphvizDOTfileforNcycles-flows:DisplayallflowsforNcycles-silent:DonotgenerateanytracingoutputforNcycles-u,--unprivilegedTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]-p,--protocol<PROTOCOL>Tracingprotocol[default:icmp]Possiblevalues:-icmp:InternetControlMessageProtocol-udp:UserDatagramProtocol-tcp:TransmissionControlProtocol--udpTraceusingtheUDPprotocol--tcpTraceusingtheTCPprotocol--icmpTraceusingtheICMPprotocol-F,--addr-family<ADDR_FAMILY>Theaddressfamily[default:Ipv4thenIpv6]Possiblevalues:-ipv4:Ipv4only-ipv6:Ipv6only-ipv6-then-ipv4:Ipv6withafallbacktoIpv4-ipv4-then-ipv6:Ipv4withafallbacktoIpv6-4,--ipv4UseIPv4only-6,--ipv6UseIPv6only-P,--target-port<TARGET_PORT>Thetargetport(TCP&UDPonly)[default:80]-S,--source-port<SOURCE_PORT>Thesourceport(TCP&UDPonly)[default:auto]-A,--source-address<SOURCE_ADDRESS>ThesourceIPaddress[default:auto]-I,--interface<INTERFACE>Thenetworkinterface[default:auto]-i,--min-round-duration<MIN_ROUND_DURATION>Theminimumdurationofeveryround[default:1s]-T,--max-round-duration<MAX_ROUND_DURATION>Themaximumdurationofeveryround[default:1s]-g,--grace-duration<GRACE_DURATION>TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]--initial-sequence<INITIAL_SEQUENCE>Theinitialsequencenumber[default:33434]-R,--multipath-strategy<MULTIPATH_STRATEGY>TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]Possiblevalues:-classic:Thesrcordestportisusedtostorethesequencenumber-paris:TheUDP`checksum`fieldisusedtostorethesequencenumber-dublin:TheIP`identifier`fieldisusedtostorethesequencenumber-U,--max-inflight<MAX_INFLIGHT>Themaximumnumberofin-flightICMPechorequests[default:24]-f,--first-ttl<FIRST_TTL>TheTTLtostartfrom[default:1]-t,--max-ttl<MAX_TTL>ThemaximumnumberofTTLhops[default:64]--packet-size<PACKET_SIZE>ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]--payload-pattern<PAYLOAD_PATTERN>TherepeatingpatterninthepayloadoftheICMPpacket[default:0]-Q,--tos<TOS>TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]-e,--icmp-extensionsParseICMPextensions--read-timeout<READ_TIMEOUT>Thesocketreadtimeout[default:10ms]-r,--dns-resolve-method<DNS_RESOLVE_METHOD>HowtoperformDNSqueries[default:system]Possiblevalues:-system:ResolveusingtheOSresolver-resolv:Resolveusingthe`/etc/resolv.conf`DNSconfiguration-google:ResolveusingtheGoogle`8.8.8.8`DNSservice-cloudflare:ResolveusingtheCloudflare`1.1.1.1`DNSservice-y,--dns-resolve-allTracetoallIPsresolvedfromDNSlookup[default:false]--dns-timeout<DNS_TIMEOUT>ThemaximumtimetowaittoperformDNSqueries[default:5s]--dns-ttl<DNS_TTL>Thetime-to-live(TTL)ofDNSentries[default:300s]-z,--dns-lookup-as-infoLookupautonomoussystem(AS)informationduringDNSqueries[default:false]-s,--max-samples<MAX_SAMPLES>Themaximumnumberofsamplestorecordperhop[default:256]--max-flows<MAX_FLOWS>Themaximumnumberofflowstorecord[default:64]-a,--tui-address-mode<TUI_ADDRESS_MODE>Howtorenderaddresses[default:host]Possiblevalues:-ip:ShowIPaddressonly-host:Showreverse-lookupDNShostnameonly-both:ShowbothIPaddressandreverse-lookupDNShostname--tui-as-mode<TUI_AS_MODE>HowtorenderASinformation[default:asn]Possiblevalues:-asn:ShowtheASN-prefix:DisplaytheASprefix-country-code:Displaythecountrycode-registry:Displaytheregistryname-allocated:Displaytheallocateddate-name:DisplaytheASname--tui-custom-columns<TUI_CUSTOM_COLUMNS>CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]--tui-icmp-extension-mode<TUI_ICMP_EXTENSION_MODE>HowtorenderICMPextensions[default:off]Possiblevalues:-off:Donotshow`icmp`extensions-mpls:ShowMPLSlabel(s)only-full:Showfull`icmp`extensiondataforallknownextensions-all:Showfull`icmp`extensiondataforallclasses--tui-geoip-mode<TUI_GEOIP_MODE>HowtorenderGeoIpinformation[default:short]Possiblevalues:-off:DonotdisplayGeoIpdata-short:Showshortformat-long:Showlongformat-location:ShowlatitudeandLongitudeformat-M,--tui-max-addrs<TUI_MAX_ADDRS>Themaximumnumberofaddressestoshowperhop[default:auto]--tui-preserve-screenPreservethescreenonexit[default:false]--tui-refresh-rate<TUI_REFRESH_RATE>TheTuirefreshrate[default:100ms]--tui-privacy-max-ttl<TUI_PRIVACY_MAX_TTL>Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]--tui-locale<TUI_LOCALE>ThelocaletousefortheTUI[default:auto]--tui-theme-colors<TUI_THEME_COLORS>TheTUIthemecolors[item=color,item=color,..]--print-tui-theme-itemsPrintallTUIthemeitemsandexit--tui-key-bindings<TUI_KEY_BINDINGS>TheTUIkeybindings[command=key,command=key,..]--print-tui-binding-commandsPrintallTUIcommandsthatcanbeboundandexit-C,--report-cycles<REPORT_CYCLES>Thenumberofreportcyclestorun[default:10]-G,--geoip-mmdb-file<GEOIP_MMDB_FILE>ThesupportedMaxMindorIPinfoGeoIpmmdbfile--generate<GENERATE>Generateshellcompletion[possiblevalues:bash,elvish,fish,powershell,zsh]--generate-manGenerateROFFmanpage--print-config-templatePrintatemplatetomlconfigfileandexit--log-format<LOG_FORMAT>Thedebuglogformat[default:pretty]Possiblevalues:-compact:Displaylogdatainacompactformat-pretty:Displaylogdatainaprettyformat-json:Displaylogdatainajsonformat-chrome:DisplaylogdatainChrometraceformat--log-filter<LOG_FILTER>Thedebuglogfilter[default:trippy=debug]--log-span-events<LOG_SPAN_EVENTS>Thedebuglogformat[default:off]Possiblevalues:-off:Donotdisplayeventspans-active:Displayenterandexiteventspans-full:Displayalleventspans-v,--verboseEnableverbosedebuglogging-h,--helpPrinthelp(seeasummarywith'-h')-V,--versionPrintversion
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip_-h.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip_-h.snap
index 64e52c1be..385b21d9c 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip_-h.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__config__tests__compare_snapshot@trip_-h.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/config.rs
 ---
-AnetworkdiagnostictoolUsage:trip[OPTIONS][TARGETS]...Arguments:[TARGETS]...AspacedelimitedlistofhostnamesandIPstotraceOptions:-c,--config-file<CONFIG_FILE>Configfile-m,--mode<MODE>Outputmode[default:tui][possiblevalues:tui,stream,pretty,markdown,csv,json,dot,flows,silent]-u,--unprivilegedTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]-p,--protocol<PROTOCOL>Tracingprotocol[default:icmp][possiblevalues:icmp,udp,tcp]--udpTraceusingtheUDPprotocol--tcpTraceusingtheTCPprotocol--icmpTraceusingtheICMPprotocol-F,--addr-family<ADDR_FAMILY>Theaddressfamily[default:Ipv4thenIpv6][possiblevalues:ipv4,ipv6,ipv6-then-ipv4,ipv4-then-ipv6]-4,--ipv4UseIPv4only-6,--ipv6UseIPv6only-P,--target-port<TARGET_PORT>Thetargetport(TCP&UDPonly)[default:80]-S,--source-port<SOURCE_PORT>Thesourceport(TCP&UDPonly)[default:auto]-A,--source-address<SOURCE_ADDRESS>ThesourceIPaddress[default:auto]-I,--interface<INTERFACE>Thenetworkinterface[default:auto]-i,--min-round-duration<MIN_ROUND_DURATION>Theminimumdurationofeveryround[default:1s]-T,--max-round-duration<MAX_ROUND_DURATION>Themaximumdurationofeveryround[default:1s]-g,--grace-duration<GRACE_DURATION>TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]--initial-sequence<INITIAL_SEQUENCE>Theinitialsequencenumber[default:33434]-R,--multipath-strategy<MULTIPATH_STRATEGY>TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic][possiblevalues:classic,paris,dublin]-U,--max-inflight<MAX_INFLIGHT>Themaximumnumberofin-flightICMPechorequests[default:24]-f,--first-ttl<FIRST_TTL>TheTTLtostartfrom[default:1]-t,--max-ttl<MAX_TTL>ThemaximumnumberofTTLhops[default:64]--packet-size<PACKET_SIZE>ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]--payload-pattern<PAYLOAD_PATTERN>TherepeatingpatterninthepayloadoftheICMPpacket[default:0]-Q,--tos<TOS>TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]-e,--icmp-extensionsParseICMPextensions--read-timeout<READ_TIMEOUT>Thesocketreadtimeout[default:10ms]-r,--dns-resolve-method<DNS_RESOLVE_METHOD>HowtoperformDNSqueries[default:system][possiblevalues:system,resolv,google,cloudflare]-y,--dns-resolve-allTracetoallIPsresolvedfromDNSlookup[default:false]--dns-timeout<DNS_TIMEOUT>ThemaximumtimetowaittoperformDNSqueries[default:5s]--dns-ttl<DNS_TTL>Thetime-to-live(TTL)ofDNSentries[default:300s]-z,--dns-lookup-as-infoLookupautonomoussystem(AS)informationduringDNSqueries[default:false]-s,--max-samples<MAX_SAMPLES>Themaximumnumberofsamplestorecordperhop[default:256]--max-flows<MAX_FLOWS>Themaximumnumberofflowstorecord[default:64]-a,--tui-address-mode<TUI_ADDRESS_MODE>Howtorenderaddresses[default:host][possiblevalues:ip,host,both]--tui-as-mode<TUI_AS_MODE>HowtorenderASinformation[default:asn][possiblevalues:asn,prefix,country-code,registry,allocated,name]--tui-custom-columns<TUI_CUSTOM_COLUMNS>CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]--tui-icmp-extension-mode<TUI_ICMP_EXTENSION_MODE>HowtorenderICMPextensions[default:off][possiblevalues:off,mpls,full,all]--tui-geoip-mode<TUI_GEOIP_MODE>HowtorenderGeoIpinformation[default:short][possiblevalues:off,short,long,location]-M,--tui-max-addrs<TUI_MAX_ADDRS>Themaximumnumberofaddressestoshowperhop[default:auto]--tui-preserve-screenPreservethescreenonexit[default:false]--tui-refresh-rate<TUI_REFRESH_RATE>TheTuirefreshrate[default:100ms]--tui-privacy-max-ttl<TUI_PRIVACY_MAX_TTL>Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]--tui-theme-colors<TUI_THEME_COLORS>TheTUIthemecolors[item=color,item=color,..]--print-tui-theme-itemsPrintallTUIthemeitemsandexit--tui-key-bindings<TUI_KEY_BINDINGS>TheTUIkeybindings[command=key,command=key,..]--print-tui-binding-commandsPrintallTUIcommandsthatcanbeboundandexit-C,--report-cycles<REPORT_CYCLES>Thenumberofreportcyclestorun[default:10]-G,--geoip-mmdb-file<GEOIP_MMDB_FILE>ThesupportedMaxMindorIPinfoGeoIpmmdbfile--generate<GENERATE>Generateshellcompletion[possiblevalues:bash,elvish,fish,powershell,zsh]--generate-manGenerateROFFmanpage--print-config-templatePrintatemplatetomlconfigfileandexit--log-format<LOG_FORMAT>Thedebuglogformat[default:pretty][possiblevalues:compact,pretty,json,chrome]--log-filter<LOG_FILTER>Thedebuglogfilter[default:trippy=debug]--log-span-events<LOG_SPAN_EVENTS>Thedebuglogformat[default:off][possiblevalues:off,active,full]-v,--verboseEnableverbosedebuglogging-h,--helpPrinthelp(seemorewith'--help')-V,--versionPrintversion
+AnetworkdiagnostictoolUsage:trip[OPTIONS][TARGETS]...Arguments:[TARGETS]...AspacedelimitedlistofhostnamesandIPstotraceOptions:-c,--config-file<CONFIG_FILE>Configfile-m,--mode<MODE>Outputmode[default:tui][possiblevalues:tui,stream,pretty,markdown,csv,json,dot,flows,silent]-u,--unprivilegedTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]-p,--protocol<PROTOCOL>Tracingprotocol[default:icmp][possiblevalues:icmp,udp,tcp]--udpTraceusingtheUDPprotocol--tcpTraceusingtheTCPprotocol--icmpTraceusingtheICMPprotocol-F,--addr-family<ADDR_FAMILY>Theaddressfamily[default:Ipv4thenIpv6][possiblevalues:ipv4,ipv6,ipv6-then-ipv4,ipv4-then-ipv6]-4,--ipv4UseIPv4only-6,--ipv6UseIPv6only-P,--target-port<TARGET_PORT>Thetargetport(TCP&UDPonly)[default:80]-S,--source-port<SOURCE_PORT>Thesourceport(TCP&UDPonly)[default:auto]-A,--source-address<SOURCE_ADDRESS>ThesourceIPaddress[default:auto]-I,--interface<INTERFACE>Thenetworkinterface[default:auto]-i,--min-round-duration<MIN_ROUND_DURATION>Theminimumdurationofeveryround[default:1s]-T,--max-round-duration<MAX_ROUND_DURATION>Themaximumdurationofeveryround[default:1s]-g,--grace-duration<GRACE_DURATION>TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]--initial-sequence<INITIAL_SEQUENCE>Theinitialsequencenumber[default:33434]-R,--multipath-strategy<MULTIPATH_STRATEGY>TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic][possiblevalues:classic,paris,dublin]-U,--max-inflight<MAX_INFLIGHT>Themaximumnumberofin-flightICMPechorequests[default:24]-f,--first-ttl<FIRST_TTL>TheTTLtostartfrom[default:1]-t,--max-ttl<MAX_TTL>ThemaximumnumberofTTLhops[default:64]--packet-size<PACKET_SIZE>ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]--payload-pattern<PAYLOAD_PATTERN>TherepeatingpatterninthepayloadoftheICMPpacket[default:0]-Q,--tos<TOS>TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]-e,--icmp-extensionsParseICMPextensions--read-timeout<READ_TIMEOUT>Thesocketreadtimeout[default:10ms]-r,--dns-resolve-method<DNS_RESOLVE_METHOD>HowtoperformDNSqueries[default:system][possiblevalues:system,resolv,google,cloudflare]-y,--dns-resolve-allTracetoallIPsresolvedfromDNSlookup[default:false]--dns-timeout<DNS_TIMEOUT>ThemaximumtimetowaittoperformDNSqueries[default:5s]--dns-ttl<DNS_TTL>Thetime-to-live(TTL)ofDNSentries[default:300s]-z,--dns-lookup-as-infoLookupautonomoussystem(AS)informationduringDNSqueries[default:false]-s,--max-samples<MAX_SAMPLES>Themaximumnumberofsamplestorecordperhop[default:256]--max-flows<MAX_FLOWS>Themaximumnumberofflowstorecord[default:64]-a,--tui-address-mode<TUI_ADDRESS_MODE>Howtorenderaddresses[default:host][possiblevalues:ip,host,both]--tui-as-mode<TUI_AS_MODE>HowtorenderASinformation[default:asn][possiblevalues:asn,prefix,country-code,registry,allocated,name]--tui-custom-columns<TUI_CUSTOM_COLUMNS>CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]--tui-icmp-extension-mode<TUI_ICMP_EXTENSION_MODE>HowtorenderICMPextensions[default:off][possiblevalues:off,mpls,full,all]--tui-geoip-mode<TUI_GEOIP_MODE>HowtorenderGeoIpinformation[default:short][possiblevalues:off,short,long,location]-M,--tui-max-addrs<TUI_MAX_ADDRS>Themaximumnumberofaddressestoshowperhop[default:auto]--tui-preserve-screenPreservethescreenonexit[default:false]--tui-refresh-rate<TUI_REFRESH_RATE>TheTuirefreshrate[default:100ms]--tui-privacy-max-ttl<TUI_PRIVACY_MAX_TTL>Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]--tui-locale<TUI_LOCALE>ThelocaletousefortheTUI[default:auto]--tui-theme-colors<TUI_THEME_COLORS>TheTUIthemecolors[item=color,item=color,..]--print-tui-theme-itemsPrintallTUIthemeitemsandexit--tui-key-bindings<TUI_KEY_BINDINGS>TheTUIkeybindings[command=key,command=key,..]--print-tui-binding-commandsPrintallTUIcommandsthatcanbeboundandexit-C,--report-cycles<REPORT_CYCLES>Thenumberofreportcyclestorun[default:10]-G,--geoip-mmdb-file<GEOIP_MMDB_FILE>ThesupportedMaxMindorIPinfoGeoIpmmdbfile--generate<GENERATE>Generateshellcompletion[possiblevalues:bash,elvish,fish,powershell,zsh]--generate-manGenerateROFFmanpage--print-config-templatePrintatemplatetomlconfigfileandexit--log-format<LOG_FORMAT>Thedebuglogformat[default:pretty][possiblevalues:compact,pretty,json,chrome]--log-filter<LOG_FILTER>Thedebuglogfilter[default:trippy=debug]--log-span-events<LOG_SPAN_EVENTS>Thedebuglogformat[default:off][possiblevalues:off,active,full]-v,--verboseEnableverbosedebuglogging-h,--helpPrinthelp(seemorewith'--help')-V,--versionPrintversion
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_bash_shell_completions.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_bash_shell_completions.snap
index a4b154aac..5dfce0a77 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_bash_shell_completions.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_bash_shell_completions.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/print.rs
 ---
-_trip(){localicurprevoptscmdCOMPREPLY=()cur="${COMP_WORDS[COMP_CWORD]}"prev="${COMP_WORDS[COMP_CWORD-1]}"cmd=""opts=""foriin${COMP_WORDS[@]}docase"${cmd},${i}"in",$1")cmd="trip";;*);;esacdonecase"${cmd}"intrip)opts="-c-m-u-p-F-4-6-P-S-A-I-i-T-g-R-U-f-t-Q-e-r-y-z-s-a-M-C-G-v-h-V--config-file--mode--unprivileged--protocol--udp--tcp--icmp--addr-family--ipv4--ipv6--target-port--source-port--source-address--interface--min-round-duration--max-round-duration--grace-duration--initial-sequence--multipath-strategy--max-inflight--first-ttl--max-ttl--packet-size--payload-pattern--tos--icmp-extensions--read-timeout--dns-resolve-method--dns-resolve-all--dns-timeout--dns-ttl--dns-lookup-as-info--max-samples--max-flows--tui-address-mode--tui-as-mode--tui-custom-columns--tui-icmp-extension-mode--tui-geoip-mode--tui-max-addrs--tui-preserve-screen--tui-refresh-rate--tui-privacy-max-ttl--tui-theme-colors--print-tui-theme-items--tui-key-bindings--print-tui-binding-commands--report-cycles--geoip-mmdb-file--generate--generate-man--print-config-template--log-format--log-filter--log-span-events--verbose--help--version[TARGETS]..."if[[${cur}==-*||${COMP_CWORD}-eq1]];thenCOMPREPLY=($(compgen-W"${opts}"--"${cur}"))return0ficase"${prev}"in--config-file)localoldifsif[-n"${IFS+x}"];thenoldifs="$IFS"fiIFS=$'\n'COMPREPLY=($(compgen-f"${cur}"))if[-n"${oldifs+x}"];thenIFS="$oldifs"fiif[["${BASH_VERSINFO[0]}"-ge4]];thencompopt-ofilenamesfireturn0;;-c)localoldifsif[-n"${IFS+x}"];thenoldifs="$IFS"fiIFS=$'\n'COMPREPLY=($(compgen-f"${cur}"))if[-n"${oldifs+x}"];thenIFS="$oldifs"fiif[["${BASH_VERSINFO[0]}"-ge4]];thencompopt-ofilenamesfireturn0;;--mode)COMPREPLY=($(compgen-W"tuistreamprettymarkdowncsvjsondotflowssilent"--"${cur}"))return0;;-m)COMPREPLY=($(compgen-W"tuistreamprettymarkdowncsvjsondotflowssilent"--"${cur}"))return0;;--protocol)COMPREPLY=($(compgen-W"icmpudptcp"--"${cur}"))return0;;-p)COMPREPLY=($(compgen-W"icmpudptcp"--"${cur}"))return0;;--addr-family)COMPREPLY=($(compgen-W"ipv4ipv6ipv6-then-ipv4ipv4-then-ipv6"--"${cur}"))return0;;-F)COMPREPLY=($(compgen-W"ipv4ipv6ipv6-then-ipv4ipv4-then-ipv6"--"${cur}"))return0;;--target-port)COMPREPLY=($(compgen-f"${cur}"))return0;;-P)COMPREPLY=($(compgen-f"${cur}"))return0;;--source-port)COMPREPLY=($(compgen-f"${cur}"))return0;;-S)COMPREPLY=($(compgen-f"${cur}"))return0;;--source-address)COMPREPLY=($(compgen-f"${cur}"))return0;;-A)COMPREPLY=($(compgen-f"${cur}"))return0;;--interface)COMPREPLY=($(compgen-f"${cur}"))return0;;-I)COMPREPLY=($(compgen-f"${cur}"))return0;;--min-round-duration)COMPREPLY=($(compgen-f"${cur}"))return0;;-i)COMPREPLY=($(compgen-f"${cur}"))return0;;--max-round-duration)COMPREPLY=($(compgen-f"${cur}"))return0;;-T)COMPREPLY=($(compgen-f"${cur}"))return0;;--grace-duration)COMPREPLY=($(compgen-f"${cur}"))return0;;-g)COMPREPLY=($(compgen-f"${cur}"))return0;;--initial-sequence)COMPREPLY=($(compgen-f"${cur}"))return0;;--multipath-strategy)COMPREPLY=($(compgen-W"classicparisdublin"--"${cur}"))return0;;-R)COMPREPLY=($(compgen-W"classicparisdublin"--"${cur}"))return0;;--max-inflight)COMPREPLY=($(compgen-f"${cur}"))return0;;-U)COMPREPLY=($(compgen-f"${cur}"))return0;;--first-ttl)COMPREPLY=($(compgen-f"${cur}"))return0;;-f)COMPREPLY=($(compgen-f"${cur}"))return0;;--max-ttl)COMPREPLY=($(compgen-f"${cur}"))return0;;-t)COMPREPLY=($(compgen-f"${cur}"))return0;;--packet-size)COMPREPLY=($(compgen-f"${cur}"))return0;;--payload-pattern)COMPREPLY=($(compgen-f"${cur}"))return0;;--tos)COMPREPLY=($(compgen-f"${cur}"))return0;;-Q)COMPREPLY=($(compgen-f"${cur}"))return0;;--read-timeout)COMPREPLY=($(compgen-f"${cur}"))return0;;--dns-resolve-method)COMPREPLY=($(compgen-W"systemresolvgooglecloudflare"--"${cur}"))return0;;-r)COMPREPLY=($(compgen-W"systemresolvgooglecloudflare"--"${cur}"))return0;;--dns-timeout)COMPREPLY=($(compgen-f"${cur}"))return0;;--dns-ttl)COMPREPLY=($(compgen-f"${cur}"))return0;;--max-samples)COMPREPLY=($(compgen-f"${cur}"))return0;;-s)COMPREPLY=($(compgen-f"${cur}"))return0;;--max-flows)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-address-mode)COMPREPLY=($(compgen-W"iphostboth"--"${cur}"))return0;;-a)COMPREPLY=($(compgen-W"iphostboth"--"${cur}"))return0;;--tui-as-mode)COMPREPLY=($(compgen-W"asnprefixcountry-coderegistryallocatedname"--"${cur}"))return0;;--tui-custom-columns)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-icmp-extension-mode)COMPREPLY=($(compgen-W"offmplsfullall"--"${cur}"))return0;;--tui-geoip-mode)COMPREPLY=($(compgen-W"offshortlonglocation"--"${cur}"))return0;;--tui-max-addrs)COMPREPLY=($(compgen-f"${cur}"))return0;;-M)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-refresh-rate)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-privacy-max-ttl)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-theme-colors)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-key-bindings)COMPREPLY=($(compgen-f"${cur}"))return0;;--report-cycles)COMPREPLY=($(compgen-f"${cur}"))return0;;-C)COMPREPLY=($(compgen-f"${cur}"))return0;;--geoip-mmdb-file)localoldifsif[-n"${IFS+x}"];thenoldifs="$IFS"fiIFS=$'\n'COMPREPLY=($(compgen-f"${cur}"))if[-n"${oldifs+x}"];thenIFS="$oldifs"fiif[["${BASH_VERSINFO[0]}"-ge4]];thencompopt-ofilenamesfireturn0;;-G)localoldifsif[-n"${IFS+x}"];thenoldifs="$IFS"fiIFS=$'\n'COMPREPLY=($(compgen-f"${cur}"))if[-n"${oldifs+x}"];thenIFS="$oldifs"fiif[["${BASH_VERSINFO[0]}"-ge4]];thencompopt-ofilenamesfireturn0;;--generate)COMPREPLY=($(compgen-W"bashelvishfishpowershellzsh"--"${cur}"))return0;;--log-format)COMPREPLY=($(compgen-W"compactprettyjsonchrome"--"${cur}"))return0;;--log-filter)COMPREPLY=($(compgen-f"${cur}"))return0;;--log-span-events)COMPREPLY=($(compgen-W"offactivefull"--"${cur}"))return0;;*)COMPREPLY=();;esacCOMPREPLY=($(compgen-W"${opts}"--"${cur}"))return0;;esac}if[["${BASH_VERSINFO[0]}"-eq4&&"${BASH_VERSINFO[1]}"-ge4||"${BASH_VERSINFO[0]}"-gt4]];thencomplete-F_trip-onosort-obashdefault-odefaulttripelsecomplete-F_trip-obashdefault-odefaulttripfi
+_trip(){localicurprevoptscmdCOMPREPLY=()cur="${COMP_WORDS[COMP_CWORD]}"prev="${COMP_WORDS[COMP_CWORD-1]}"cmd=""opts=""foriin${COMP_WORDS[@]}docase"${cmd},${i}"in",$1")cmd="trip";;*);;esacdonecase"${cmd}"intrip)opts="-c-m-u-p-F-4-6-P-S-A-I-i-T-g-R-U-f-t-Q-e-r-y-z-s-a-M-C-G-v-h-V--config-file--mode--unprivileged--protocol--udp--tcp--icmp--addr-family--ipv4--ipv6--target-port--source-port--source-address--interface--min-round-duration--max-round-duration--grace-duration--initial-sequence--multipath-strategy--max-inflight--first-ttl--max-ttl--packet-size--payload-pattern--tos--icmp-extensions--read-timeout--dns-resolve-method--dns-resolve-all--dns-timeout--dns-ttl--dns-lookup-as-info--max-samples--max-flows--tui-address-mode--tui-as-mode--tui-custom-columns--tui-icmp-extension-mode--tui-geoip-mode--tui-max-addrs--tui-preserve-screen--tui-refresh-rate--tui-privacy-max-ttl--tui-locale--tui-theme-colors--print-tui-theme-items--tui-key-bindings--print-tui-binding-commands--report-cycles--geoip-mmdb-file--generate--generate-man--print-config-template--log-format--log-filter--log-span-events--verbose--help--version[TARGETS]..."if[[${cur}==-*||${COMP_CWORD}-eq1]];thenCOMPREPLY=($(compgen-W"${opts}"--"${cur}"))return0ficase"${prev}"in--config-file)localoldifsif[-n"${IFS+x}"];thenoldifs="$IFS"fiIFS=$'\n'COMPREPLY=($(compgen-f"${cur}"))if[-n"${oldifs+x}"];thenIFS="$oldifs"fiif[["${BASH_VERSINFO[0]}"-ge4]];thencompopt-ofilenamesfireturn0;;-c)localoldifsif[-n"${IFS+x}"];thenoldifs="$IFS"fiIFS=$'\n'COMPREPLY=($(compgen-f"${cur}"))if[-n"${oldifs+x}"];thenIFS="$oldifs"fiif[["${BASH_VERSINFO[0]}"-ge4]];thencompopt-ofilenamesfireturn0;;--mode)COMPREPLY=($(compgen-W"tuistreamprettymarkdowncsvjsondotflowssilent"--"${cur}"))return0;;-m)COMPREPLY=($(compgen-W"tuistreamprettymarkdowncsvjsondotflowssilent"--"${cur}"))return0;;--protocol)COMPREPLY=($(compgen-W"icmpudptcp"--"${cur}"))return0;;-p)COMPREPLY=($(compgen-W"icmpudptcp"--"${cur}"))return0;;--addr-family)COMPREPLY=($(compgen-W"ipv4ipv6ipv6-then-ipv4ipv4-then-ipv6"--"${cur}"))return0;;-F)COMPREPLY=($(compgen-W"ipv4ipv6ipv6-then-ipv4ipv4-then-ipv6"--"${cur}"))return0;;--target-port)COMPREPLY=($(compgen-f"${cur}"))return0;;-P)COMPREPLY=($(compgen-f"${cur}"))return0;;--source-port)COMPREPLY=($(compgen-f"${cur}"))return0;;-S)COMPREPLY=($(compgen-f"${cur}"))return0;;--source-address)COMPREPLY=($(compgen-f"${cur}"))return0;;-A)COMPREPLY=($(compgen-f"${cur}"))return0;;--interface)COMPREPLY=($(compgen-f"${cur}"))return0;;-I)COMPREPLY=($(compgen-f"${cur}"))return0;;--min-round-duration)COMPREPLY=($(compgen-f"${cur}"))return0;;-i)COMPREPLY=($(compgen-f"${cur}"))return0;;--max-round-duration)COMPREPLY=($(compgen-f"${cur}"))return0;;-T)COMPREPLY=($(compgen-f"${cur}"))return0;;--grace-duration)COMPREPLY=($(compgen-f"${cur}"))return0;;-g)COMPREPLY=($(compgen-f"${cur}"))return0;;--initial-sequence)COMPREPLY=($(compgen-f"${cur}"))return0;;--multipath-strategy)COMPREPLY=($(compgen-W"classicparisdublin"--"${cur}"))return0;;-R)COMPREPLY=($(compgen-W"classicparisdublin"--"${cur}"))return0;;--max-inflight)COMPREPLY=($(compgen-f"${cur}"))return0;;-U)COMPREPLY=($(compgen-f"${cur}"))return0;;--first-ttl)COMPREPLY=($(compgen-f"${cur}"))return0;;-f)COMPREPLY=($(compgen-f"${cur}"))return0;;--max-ttl)COMPREPLY=($(compgen-f"${cur}"))return0;;-t)COMPREPLY=($(compgen-f"${cur}"))return0;;--packet-size)COMPREPLY=($(compgen-f"${cur}"))return0;;--payload-pattern)COMPREPLY=($(compgen-f"${cur}"))return0;;--tos)COMPREPLY=($(compgen-f"${cur}"))return0;;-Q)COMPREPLY=($(compgen-f"${cur}"))return0;;--read-timeout)COMPREPLY=($(compgen-f"${cur}"))return0;;--dns-resolve-method)COMPREPLY=($(compgen-W"systemresolvgooglecloudflare"--"${cur}"))return0;;-r)COMPREPLY=($(compgen-W"systemresolvgooglecloudflare"--"${cur}"))return0;;--dns-timeout)COMPREPLY=($(compgen-f"${cur}"))return0;;--dns-ttl)COMPREPLY=($(compgen-f"${cur}"))return0;;--max-samples)COMPREPLY=($(compgen-f"${cur}"))return0;;-s)COMPREPLY=($(compgen-f"${cur}"))return0;;--max-flows)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-address-mode)COMPREPLY=($(compgen-W"iphostboth"--"${cur}"))return0;;-a)COMPREPLY=($(compgen-W"iphostboth"--"${cur}"))return0;;--tui-as-mode)COMPREPLY=($(compgen-W"asnprefixcountry-coderegistryallocatedname"--"${cur}"))return0;;--tui-custom-columns)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-icmp-extension-mode)COMPREPLY=($(compgen-W"offmplsfullall"--"${cur}"))return0;;--tui-geoip-mode)COMPREPLY=($(compgen-W"offshortlonglocation"--"${cur}"))return0;;--tui-max-addrs)COMPREPLY=($(compgen-f"${cur}"))return0;;-M)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-refresh-rate)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-privacy-max-ttl)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-locale)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-theme-colors)COMPREPLY=($(compgen-f"${cur}"))return0;;--tui-key-bindings)COMPREPLY=($(compgen-f"${cur}"))return0;;--report-cycles)COMPREPLY=($(compgen-f"${cur}"))return0;;-C)COMPREPLY=($(compgen-f"${cur}"))return0;;--geoip-mmdb-file)localoldifsif[-n"${IFS+x}"];thenoldifs="$IFS"fiIFS=$'\n'COMPREPLY=($(compgen-f"${cur}"))if[-n"${oldifs+x}"];thenIFS="$oldifs"fiif[["${BASH_VERSINFO[0]}"-ge4]];thencompopt-ofilenamesfireturn0;;-G)localoldifsif[-n"${IFS+x}"];thenoldifs="$IFS"fiIFS=$'\n'COMPREPLY=($(compgen-f"${cur}"))if[-n"${oldifs+x}"];thenIFS="$oldifs"fiif[["${BASH_VERSINFO[0]}"-ge4]];thencompopt-ofilenamesfireturn0;;--generate)COMPREPLY=($(compgen-W"bashelvishfishpowershellzsh"--"${cur}"))return0;;--log-format)COMPREPLY=($(compgen-W"compactprettyjsonchrome"--"${cur}"))return0;;--log-filter)COMPREPLY=($(compgen-f"${cur}"))return0;;--log-span-events)COMPREPLY=($(compgen-W"offactivefull"--"${cur}"))return0;;*)COMPREPLY=();;esacCOMPREPLY=($(compgen-W"${opts}"--"${cur}"))return0;;esac}if[["${BASH_VERSINFO[0]}"-eq4&&"${BASH_VERSINFO[1]}"-ge4||"${BASH_VERSINFO[0]}"-gt4]];thencomplete-F_trip-onosort-obashdefault-odefaulttripelsecomplete-F_trip-obashdefault-odefaulttripfi
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_elvish_shell_completions.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_elvish_shell_completions.snap
index 936126065..11a0664bd 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_elvish_shell_completions.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_elvish_shell_completions.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/print.rs
 ---
-usebuiltin;usestr;setedit:completion:arg-completer[trip]={|@words|fnspaces{|n|builtin:repeat$n''|str:join''}fncand{|textdesc|edit:complex-candidate$text&display=$text''(spaces(-14(wcswidth$text)))$desc}varcommand='trip'forword$words[1..-1]{if(str:has-prefix$word'-'){break}setcommand=$command';'$word}varcompletions=[&'trip'={cand-c'Configfile'cand--config-file'Configfile'cand-m'Outputmode[default:tui]'cand--mode'Outputmode[default:tui]'cand-p'Tracingprotocol[default:icmp]'cand--protocol'Tracingprotocol[default:icmp]'cand-F'Theaddressfamily[default:Ipv4thenIpv6]'cand--addr-family'Theaddressfamily[default:Ipv4thenIpv6]'cand-P'Thetargetport(TCP&UDPonly)[default:80]'cand--target-port'Thetargetport(TCP&UDPonly)[default:80]'cand-S'Thesourceport(TCP&UDPonly)[default:auto]'cand--source-port'Thesourceport(TCP&UDPonly)[default:auto]'cand-A'ThesourceIPaddress[default:auto]'cand--source-address'ThesourceIPaddress[default:auto]'cand-I'Thenetworkinterface[default:auto]'cand--interface'Thenetworkinterface[default:auto]'cand-i'Theminimumdurationofeveryround[default:1s]'cand--min-round-duration'Theminimumdurationofeveryround[default:1s]'cand-T'Themaximumdurationofeveryround[default:1s]'cand--max-round-duration'Themaximumdurationofeveryround[default:1s]'cand-g'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]'cand--grace-duration'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]'cand--initial-sequence'Theinitialsequencenumber[default:33434]'cand-R'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]'cand--multipath-strategy'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]'cand-U'Themaximumnumberofin-flightICMPechorequests[default:24]'cand--max-inflight'Themaximumnumberofin-flightICMPechorequests[default:24]'cand-f'TheTTLtostartfrom[default:1]'cand--first-ttl'TheTTLtostartfrom[default:1]'cand-t'ThemaximumnumberofTTLhops[default:64]'cand--max-ttl'ThemaximumnumberofTTLhops[default:64]'cand--packet-size'ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]'cand--payload-pattern'TherepeatingpatterninthepayloadoftheICMPpacket[default:0]'cand-Q'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]'cand--tos'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]'cand--read-timeout'Thesocketreadtimeout[default:10ms]'cand-r'HowtoperformDNSqueries[default:system]'cand--dns-resolve-method'HowtoperformDNSqueries[default:system]'cand--dns-timeout'ThemaximumtimetowaittoperformDNSqueries[default:5s]'cand--dns-ttl'Thetime-to-live(TTL)ofDNSentries[default:300s]'cand-s'Themaximumnumberofsamplestorecordperhop[default:256]'cand--max-samples'Themaximumnumberofsamplestorecordperhop[default:256]'cand--max-flows'Themaximumnumberofflowstorecord[default:64]'cand-a'Howtorenderaddresses[default:host]'cand--tui-address-mode'Howtorenderaddresses[default:host]'cand--tui-as-mode'HowtorenderASinformation[default:asn]'cand--tui-custom-columns'CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]'cand--tui-icmp-extension-mode'HowtorenderICMPextensions[default:off]'cand--tui-geoip-mode'HowtorenderGeoIpinformation[default:short]'cand-M'Themaximumnumberofaddressestoshowperhop[default:auto]'cand--tui-max-addrs'Themaximumnumberofaddressestoshowperhop[default:auto]'cand--tui-refresh-rate'TheTuirefreshrate[default:100ms]'cand--tui-privacy-max-ttl'Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]'cand--tui-theme-colors'TheTUIthemecolors[item=color,item=color,..]'cand--tui-key-bindings'TheTUIkeybindings[command=key,command=key,..]'cand-C'Thenumberofreportcyclestorun[default:10]'cand--report-cycles'Thenumberofreportcyclestorun[default:10]'cand-G'ThesupportedMaxMindorIPinfoGeoIpmmdbfile'cand--geoip-mmdb-file'ThesupportedMaxMindorIPinfoGeoIpmmdbfile'cand--generate'Generateshellcompletion'cand--log-format'Thedebuglogformat[default:pretty]'cand--log-filter'Thedebuglogfilter[default:trippy=debug]'cand--log-span-events'Thedebuglogformat[default:off]'cand-u'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]'cand--unprivileged'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]'cand--udp'TraceusingtheUDPprotocol'cand--tcp'TraceusingtheTCPprotocol'cand--icmp'TraceusingtheICMPprotocol'cand-4'UseIPv4only'cand--ipv4'UseIPv4only'cand-6'UseIPv6only'cand--ipv6'UseIPv6only'cand-e'ParseICMPextensions'cand--icmp-extensions'ParseICMPextensions'cand-y'TracetoallIPsresolvedfromDNSlookup[default:false]'cand--dns-resolve-all'TracetoallIPsresolvedfromDNSlookup[default:false]'cand-z'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]'cand--dns-lookup-as-info'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]'cand--tui-preserve-screen'Preservethescreenonexit[default:false]'cand--print-tui-theme-items'PrintallTUIthemeitemsandexit'cand--print-tui-binding-commands'PrintallTUIcommandsthatcanbeboundandexit'cand--generate-man'GenerateROFFmanpage'cand--print-config-template'Printatemplatetomlconfigfileandexit'cand-v'Enableverbosedebuglogging'cand--verbose'Enableverbosedebuglogging'cand-h'Printhelp(seemorewith''--help'')'cand--help'Printhelp(seemorewith''--help'')'cand-V'Printversion'cand--version'Printversion'}]$completions[$command]}
+usebuiltin;usestr;setedit:completion:arg-completer[trip]={|@words|fnspaces{|n|builtin:repeat$n''|str:join''}fncand{|textdesc|edit:complex-candidate$text&display=$text''(spaces(-14(wcswidth$text)))$desc}varcommand='trip'forword$words[1..-1]{if(str:has-prefix$word'-'){break}setcommand=$command';'$word}varcompletions=[&'trip'={cand-c'Configfile'cand--config-file'Configfile'cand-m'Outputmode[default:tui]'cand--mode'Outputmode[default:tui]'cand-p'Tracingprotocol[default:icmp]'cand--protocol'Tracingprotocol[default:icmp]'cand-F'Theaddressfamily[default:Ipv4thenIpv6]'cand--addr-family'Theaddressfamily[default:Ipv4thenIpv6]'cand-P'Thetargetport(TCP&UDPonly)[default:80]'cand--target-port'Thetargetport(TCP&UDPonly)[default:80]'cand-S'Thesourceport(TCP&UDPonly)[default:auto]'cand--source-port'Thesourceport(TCP&UDPonly)[default:auto]'cand-A'ThesourceIPaddress[default:auto]'cand--source-address'ThesourceIPaddress[default:auto]'cand-I'Thenetworkinterface[default:auto]'cand--interface'Thenetworkinterface[default:auto]'cand-i'Theminimumdurationofeveryround[default:1s]'cand--min-round-duration'Theminimumdurationofeveryround[default:1s]'cand-T'Themaximumdurationofeveryround[default:1s]'cand--max-round-duration'Themaximumdurationofeveryround[default:1s]'cand-g'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]'cand--grace-duration'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]'cand--initial-sequence'Theinitialsequencenumber[default:33434]'cand-R'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]'cand--multipath-strategy'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]'cand-U'Themaximumnumberofin-flightICMPechorequests[default:24]'cand--max-inflight'Themaximumnumberofin-flightICMPechorequests[default:24]'cand-f'TheTTLtostartfrom[default:1]'cand--first-ttl'TheTTLtostartfrom[default:1]'cand-t'ThemaximumnumberofTTLhops[default:64]'cand--max-ttl'ThemaximumnumberofTTLhops[default:64]'cand--packet-size'ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]'cand--payload-pattern'TherepeatingpatterninthepayloadoftheICMPpacket[default:0]'cand-Q'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]'cand--tos'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]'cand--read-timeout'Thesocketreadtimeout[default:10ms]'cand-r'HowtoperformDNSqueries[default:system]'cand--dns-resolve-method'HowtoperformDNSqueries[default:system]'cand--dns-timeout'ThemaximumtimetowaittoperformDNSqueries[default:5s]'cand--dns-ttl'Thetime-to-live(TTL)ofDNSentries[default:300s]'cand-s'Themaximumnumberofsamplestorecordperhop[default:256]'cand--max-samples'Themaximumnumberofsamplestorecordperhop[default:256]'cand--max-flows'Themaximumnumberofflowstorecord[default:64]'cand-a'Howtorenderaddresses[default:host]'cand--tui-address-mode'Howtorenderaddresses[default:host]'cand--tui-as-mode'HowtorenderASinformation[default:asn]'cand--tui-custom-columns'CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]'cand--tui-icmp-extension-mode'HowtorenderICMPextensions[default:off]'cand--tui-geoip-mode'HowtorenderGeoIpinformation[default:short]'cand-M'Themaximumnumberofaddressestoshowperhop[default:auto]'cand--tui-max-addrs'Themaximumnumberofaddressestoshowperhop[default:auto]'cand--tui-refresh-rate'TheTuirefreshrate[default:100ms]'cand--tui-privacy-max-ttl'Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]'cand--tui-locale'ThelocaletousefortheTUI[default:auto]'cand--tui-theme-colors'TheTUIthemecolors[item=color,item=color,..]'cand--tui-key-bindings'TheTUIkeybindings[command=key,command=key,..]'cand-C'Thenumberofreportcyclestorun[default:10]'cand--report-cycles'Thenumberofreportcyclestorun[default:10]'cand-G'ThesupportedMaxMindorIPinfoGeoIpmmdbfile'cand--geoip-mmdb-file'ThesupportedMaxMindorIPinfoGeoIpmmdbfile'cand--generate'Generateshellcompletion'cand--log-format'Thedebuglogformat[default:pretty]'cand--log-filter'Thedebuglogfilter[default:trippy=debug]'cand--log-span-events'Thedebuglogformat[default:off]'cand-u'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]'cand--unprivileged'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]'cand--udp'TraceusingtheUDPprotocol'cand--tcp'TraceusingtheTCPprotocol'cand--icmp'TraceusingtheICMPprotocol'cand-4'UseIPv4only'cand--ipv4'UseIPv4only'cand-6'UseIPv6only'cand--ipv6'UseIPv6only'cand-e'ParseICMPextensions'cand--icmp-extensions'ParseICMPextensions'cand-y'TracetoallIPsresolvedfromDNSlookup[default:false]'cand--dns-resolve-all'TracetoallIPsresolvedfromDNSlookup[default:false]'cand-z'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]'cand--dns-lookup-as-info'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]'cand--tui-preserve-screen'Preservethescreenonexit[default:false]'cand--print-tui-theme-items'PrintallTUIthemeitemsandexit'cand--print-tui-binding-commands'PrintallTUIcommandsthatcanbeboundandexit'cand--generate-man'GenerateROFFmanpage'cand--print-config-template'Printatemplatetomlconfigfileandexit'cand-v'Enableverbosedebuglogging'cand--verbose'Enableverbosedebuglogging'cand-h'Printhelp(seemorewith''--help'')'cand--help'Printhelp(seemorewith''--help'')'cand-V'Printversion'cand--version'Printversion'}]$completions[$command]}
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_fish_shell_completions.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_fish_shell_completions.snap
index 36225ff44..9809947c8 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_fish_shell_completions.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_fish_shell_completions.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/print.rs
 ---
-complete-ctrip-sc-lconfig-file-d'Configfile'-r-Fcomplete-ctrip-sm-lmode-d'Outputmode[default:tui]'-r-f-a"{tui\t'DisplayinteractiveTUI',stream\t'Displayacontinuousstreamoftracingdata',pretty\t'GenerateaprettytexttablereportforNcycles',markdown\t'GenerateaMarkdowntexttablereportforNcycles',csv\t'GenerateaCSVreportforNcycles',json\t'GenerateaJSONreportforNcycles',dot\t'GenerateaGraphvizDOTfileforNcycles',flows\t'DisplayallflowsforNcycles',silent\t'DonotgenerateanytracingoutputforNcycles'}"complete-ctrip-sp-lprotocol-d'Tracingprotocol[default:icmp]'-r-f-a"{icmp\t'InternetControlMessageProtocol',udp\t'UserDatagramProtocol',tcp\t'TransmissionControlProtocol'}"complete-ctrip-sF-laddr-family-d'Theaddressfamily[default:Ipv4thenIpv6]'-r-f-a"{ipv4\t'Ipv4only',ipv6\t'Ipv6only',ipv6-then-ipv4\t'Ipv6withafallbacktoIpv4',ipv4-then-ipv6\t'Ipv4withafallbacktoIpv6'}"complete-ctrip-sP-ltarget-port-d'Thetargetport(TCP&UDPonly)[default:80]'-rcomplete-ctrip-sS-lsource-port-d'Thesourceport(TCP&UDPonly)[default:auto]'-rcomplete-ctrip-sA-lsource-address-d'ThesourceIPaddress[default:auto]'-rcomplete-ctrip-sI-linterface-d'Thenetworkinterface[default:auto]'-rcomplete-ctrip-si-lmin-round-duration-d'Theminimumdurationofeveryround[default:1s]'-rcomplete-ctrip-sT-lmax-round-duration-d'Themaximumdurationofeveryround[default:1s]'-rcomplete-ctrip-sg-lgrace-duration-d'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]'-rcomplete-ctrip-linitial-sequence-d'Theinitialsequencenumber[default:33434]'-rcomplete-ctrip-sR-lmultipath-strategy-d'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]'-r-f-a"{classic\t'Thesrcordestportisusedtostorethesequencenumber',paris\t'TheUDP`checksum`fieldisusedtostorethesequencenumber',dublin\t'TheIP`identifier`fieldisusedtostorethesequencenumber'}"complete-ctrip-sU-lmax-inflight-d'Themaximumnumberofin-flightICMPechorequests[default:24]'-rcomplete-ctrip-sf-lfirst-ttl-d'TheTTLtostartfrom[default:1]'-rcomplete-ctrip-st-lmax-ttl-d'ThemaximumnumberofTTLhops[default:64]'-rcomplete-ctrip-lpacket-size-d'ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]'-rcomplete-ctrip-lpayload-pattern-d'TherepeatingpatterninthepayloadoftheICMPpacket[default:0]'-rcomplete-ctrip-sQ-ltos-d'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]'-rcomplete-ctrip-lread-timeout-d'Thesocketreadtimeout[default:10ms]'-rcomplete-ctrip-sr-ldns-resolve-method-d'HowtoperformDNSqueries[default:system]'-r-f-a"{system\t'ResolveusingtheOSresolver',resolv\t'Resolveusingthe`/etc/resolv.conf`DNSconfiguration',google\t'ResolveusingtheGoogle`8.8.8.8`DNSservice',cloudflare\t'ResolveusingtheCloudflare`1.1.1.1`DNSservice'}"complete-ctrip-ldns-timeout-d'ThemaximumtimetowaittoperformDNSqueries[default:5s]'-rcomplete-ctrip-ldns-ttl-d'Thetime-to-live(TTL)ofDNSentries[default:300s]'-rcomplete-ctrip-ss-lmax-samples-d'Themaximumnumberofsamplestorecordperhop[default:256]'-rcomplete-ctrip-lmax-flows-d'Themaximumnumberofflowstorecord[default:64]'-rcomplete-ctrip-sa-ltui-address-mode-d'Howtorenderaddresses[default:host]'-r-f-a"{ip\t'ShowIPaddressonly',host\t'Showreverse-lookupDNShostnameonly',both\t'ShowbothIPaddressandreverse-lookupDNShostname'}"complete-ctrip-ltui-as-mode-d'HowtorenderASinformation[default:asn]'-r-f-a"{asn\t'ShowtheASN',prefix\t'DisplaytheASprefix',country-code\t'Displaythecountrycode',registry\t'Displaytheregistryname',allocated\t'Displaytheallocateddate',name\t'DisplaytheASname'}"complete-ctrip-ltui-custom-columns-d'CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]'-rcomplete-ctrip-ltui-icmp-extension-mode-d'HowtorenderICMPextensions[default:off]'-r-f-a"{off\t'Donotshow`icmp`extensions',mpls\t'ShowMPLSlabel(s)only',full\t'Showfull`icmp`extensiondataforallknownextensions',all\t'Showfull`icmp`extensiondataforallclasses'}"complete-ctrip-ltui-geoip-mode-d'HowtorenderGeoIpinformation[default:short]'-r-f-a"{off\t'DonotdisplayGeoIpdata',short\t'Showshortformat',long\t'Showlongformat',location\t'ShowlatitudeandLongitudeformat'}"complete-ctrip-sM-ltui-max-addrs-d'Themaximumnumberofaddressestoshowperhop[default:auto]'-rcomplete-ctrip-ltui-refresh-rate-d'TheTuirefreshrate[default:100ms]'-rcomplete-ctrip-ltui-privacy-max-ttl-d'Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]'-rcomplete-ctrip-ltui-theme-colors-d'TheTUIthemecolors[item=color,item=color,..]'-rcomplete-ctrip-ltui-key-bindings-d'TheTUIkeybindings[command=key,command=key,..]'-rcomplete-ctrip-sC-lreport-cycles-d'Thenumberofreportcyclestorun[default:10]'-rcomplete-ctrip-sG-lgeoip-mmdb-file-d'ThesupportedMaxMindorIPinfoGeoIpmmdbfile'-r-Fcomplete-ctrip-lgenerate-d'Generateshellcompletion'-r-f-a"{bash\t'',elvish\t'',fish\t'',powershell\t'',zsh\t''}"complete-ctrip-llog-format-d'Thedebuglogformat[default:pretty]'-r-f-a"{compact\t'Displaylogdatainacompactformat',pretty\t'Displaylogdatainaprettyformat',json\t'Displaylogdatainajsonformat',chrome\t'DisplaylogdatainChrometraceformat'}"complete-ctrip-llog-filter-d'Thedebuglogfilter[default:trippy=debug]'-rcomplete-ctrip-llog-span-events-d'Thedebuglogformat[default:off]'-r-f-a"{off\t'Donotdisplayeventspans',active\t'Displayenterandexiteventspans',full\t'Displayalleventspans'}"complete-ctrip-su-lunprivileged-d'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]'complete-ctrip-ludp-d'TraceusingtheUDPprotocol'complete-ctrip-ltcp-d'TraceusingtheTCPprotocol'complete-ctrip-licmp-d'TraceusingtheICMPprotocol'complete-ctrip-s4-lipv4-d'UseIPv4only'complete-ctrip-s6-lipv6-d'UseIPv6only'complete-ctrip-se-licmp-extensions-d'ParseICMPextensions'complete-ctrip-sy-ldns-resolve-all-d'TracetoallIPsresolvedfromDNSlookup[default:false]'complete-ctrip-sz-ldns-lookup-as-info-d'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]'complete-ctrip-ltui-preserve-screen-d'Preservethescreenonexit[default:false]'complete-ctrip-lprint-tui-theme-items-d'PrintallTUIthemeitemsandexit'complete-ctrip-lprint-tui-binding-commands-d'PrintallTUIcommandsthatcanbeboundandexit'complete-ctrip-lgenerate-man-d'GenerateROFFmanpage'complete-ctrip-lprint-config-template-d'Printatemplatetomlconfigfileandexit'complete-ctrip-sv-lverbose-d'Enableverbosedebuglogging'complete-ctrip-sh-lhelp-d'Printhelp(seemorewith\'--help\')'complete-ctrip-sV-lversion-d'Printversion'
+complete-ctrip-sc-lconfig-file-d'Configfile'-r-Fcomplete-ctrip-sm-lmode-d'Outputmode[default:tui]'-r-f-a"{tui\t'DisplayinteractiveTUI',stream\t'Displayacontinuousstreamoftracingdata',pretty\t'GenerateaprettytexttablereportforNcycles',markdown\t'GenerateaMarkdowntexttablereportforNcycles',csv\t'GenerateaCSVreportforNcycles',json\t'GenerateaJSONreportforNcycles',dot\t'GenerateaGraphvizDOTfileforNcycles',flows\t'DisplayallflowsforNcycles',silent\t'DonotgenerateanytracingoutputforNcycles'}"complete-ctrip-sp-lprotocol-d'Tracingprotocol[default:icmp]'-r-f-a"{icmp\t'InternetControlMessageProtocol',udp\t'UserDatagramProtocol',tcp\t'TransmissionControlProtocol'}"complete-ctrip-sF-laddr-family-d'Theaddressfamily[default:Ipv4thenIpv6]'-r-f-a"{ipv4\t'Ipv4only',ipv6\t'Ipv6only',ipv6-then-ipv4\t'Ipv6withafallbacktoIpv4',ipv4-then-ipv6\t'Ipv4withafallbacktoIpv6'}"complete-ctrip-sP-ltarget-port-d'Thetargetport(TCP&UDPonly)[default:80]'-rcomplete-ctrip-sS-lsource-port-d'Thesourceport(TCP&UDPonly)[default:auto]'-rcomplete-ctrip-sA-lsource-address-d'ThesourceIPaddress[default:auto]'-rcomplete-ctrip-sI-linterface-d'Thenetworkinterface[default:auto]'-rcomplete-ctrip-si-lmin-round-duration-d'Theminimumdurationofeveryround[default:1s]'-rcomplete-ctrip-sT-lmax-round-duration-d'Themaximumdurationofeveryround[default:1s]'-rcomplete-ctrip-sg-lgrace-duration-d'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]'-rcomplete-ctrip-linitial-sequence-d'Theinitialsequencenumber[default:33434]'-rcomplete-ctrip-sR-lmultipath-strategy-d'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]'-r-f-a"{classic\t'Thesrcordestportisusedtostorethesequencenumber',paris\t'TheUDP`checksum`fieldisusedtostorethesequencenumber',dublin\t'TheIP`identifier`fieldisusedtostorethesequencenumber'}"complete-ctrip-sU-lmax-inflight-d'Themaximumnumberofin-flightICMPechorequests[default:24]'-rcomplete-ctrip-sf-lfirst-ttl-d'TheTTLtostartfrom[default:1]'-rcomplete-ctrip-st-lmax-ttl-d'ThemaximumnumberofTTLhops[default:64]'-rcomplete-ctrip-lpacket-size-d'ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]'-rcomplete-ctrip-lpayload-pattern-d'TherepeatingpatterninthepayloadoftheICMPpacket[default:0]'-rcomplete-ctrip-sQ-ltos-d'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]'-rcomplete-ctrip-lread-timeout-d'Thesocketreadtimeout[default:10ms]'-rcomplete-ctrip-sr-ldns-resolve-method-d'HowtoperformDNSqueries[default:system]'-r-f-a"{system\t'ResolveusingtheOSresolver',resolv\t'Resolveusingthe`/etc/resolv.conf`DNSconfiguration',google\t'ResolveusingtheGoogle`8.8.8.8`DNSservice',cloudflare\t'ResolveusingtheCloudflare`1.1.1.1`DNSservice'}"complete-ctrip-ldns-timeout-d'ThemaximumtimetowaittoperformDNSqueries[default:5s]'-rcomplete-ctrip-ldns-ttl-d'Thetime-to-live(TTL)ofDNSentries[default:300s]'-rcomplete-ctrip-ss-lmax-samples-d'Themaximumnumberofsamplestorecordperhop[default:256]'-rcomplete-ctrip-lmax-flows-d'Themaximumnumberofflowstorecord[default:64]'-rcomplete-ctrip-sa-ltui-address-mode-d'Howtorenderaddresses[default:host]'-r-f-a"{ip\t'ShowIPaddressonly',host\t'Showreverse-lookupDNShostnameonly',both\t'ShowbothIPaddressandreverse-lookupDNShostname'}"complete-ctrip-ltui-as-mode-d'HowtorenderASinformation[default:asn]'-r-f-a"{asn\t'ShowtheASN',prefix\t'DisplaytheASprefix',country-code\t'Displaythecountrycode',registry\t'Displaytheregistryname',allocated\t'Displaytheallocateddate',name\t'DisplaytheASname'}"complete-ctrip-ltui-custom-columns-d'CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]'-rcomplete-ctrip-ltui-icmp-extension-mode-d'HowtorenderICMPextensions[default:off]'-r-f-a"{off\t'Donotshow`icmp`extensions',mpls\t'ShowMPLSlabel(s)only',full\t'Showfull`icmp`extensiondataforallknownextensions',all\t'Showfull`icmp`extensiondataforallclasses'}"complete-ctrip-ltui-geoip-mode-d'HowtorenderGeoIpinformation[default:short]'-r-f-a"{off\t'DonotdisplayGeoIpdata',short\t'Showshortformat',long\t'Showlongformat',location\t'ShowlatitudeandLongitudeformat'}"complete-ctrip-sM-ltui-max-addrs-d'Themaximumnumberofaddressestoshowperhop[default:auto]'-rcomplete-ctrip-ltui-refresh-rate-d'TheTuirefreshrate[default:100ms]'-rcomplete-ctrip-ltui-privacy-max-ttl-d'Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]'-rcomplete-ctrip-ltui-locale-d'ThelocaletousefortheTUI[default:auto]'-rcomplete-ctrip-ltui-theme-colors-d'TheTUIthemecolors[item=color,item=color,..]'-rcomplete-ctrip-ltui-key-bindings-d'TheTUIkeybindings[command=key,command=key,..]'-rcomplete-ctrip-sC-lreport-cycles-d'Thenumberofreportcyclestorun[default:10]'-rcomplete-ctrip-sG-lgeoip-mmdb-file-d'ThesupportedMaxMindorIPinfoGeoIpmmdbfile'-r-Fcomplete-ctrip-lgenerate-d'Generateshellcompletion'-r-f-a"{bash\t'',elvish\t'',fish\t'',powershell\t'',zsh\t''}"complete-ctrip-llog-format-d'Thedebuglogformat[default:pretty]'-r-f-a"{compact\t'Displaylogdatainacompactformat',pretty\t'Displaylogdatainaprettyformat',json\t'Displaylogdatainajsonformat',chrome\t'DisplaylogdatainChrometraceformat'}"complete-ctrip-llog-filter-d'Thedebuglogfilter[default:trippy=debug]'-rcomplete-ctrip-llog-span-events-d'Thedebuglogformat[default:off]'-r-f-a"{off\t'Donotdisplayeventspans',active\t'Displayenterandexiteventspans',full\t'Displayalleventspans'}"complete-ctrip-su-lunprivileged-d'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]'complete-ctrip-ludp-d'TraceusingtheUDPprotocol'complete-ctrip-ltcp-d'TraceusingtheTCPprotocol'complete-ctrip-licmp-d'TraceusingtheICMPprotocol'complete-ctrip-s4-lipv4-d'UseIPv4only'complete-ctrip-s6-lipv6-d'UseIPv6only'complete-ctrip-se-licmp-extensions-d'ParseICMPextensions'complete-ctrip-sy-ldns-resolve-all-d'TracetoallIPsresolvedfromDNSlookup[default:false]'complete-ctrip-sz-ldns-lookup-as-info-d'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]'complete-ctrip-ltui-preserve-screen-d'Preservethescreenonexit[default:false]'complete-ctrip-lprint-tui-theme-items-d'PrintallTUIthemeitemsandexit'complete-ctrip-lprint-tui-binding-commands-d'PrintallTUIcommandsthatcanbeboundandexit'complete-ctrip-lgenerate-man-d'GenerateROFFmanpage'complete-ctrip-lprint-config-template-d'Printatemplatetomlconfigfileandexit'complete-ctrip-sv-lverbose-d'Enableverbosedebuglogging'complete-ctrip-sh-lhelp-d'Printhelp(seemorewith\'--help\')'complete-ctrip-sV-lversion-d'Printversion'
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_man_page.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_man_page.snap
index 50ae83fde..8a5a8f259 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_man_page.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_man_page.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/print.rs
 ---
-.ie\n(.g.dsAq\(aq.el.dsAq'.THtrip1"trip0.12.0-dev".SHNAMEtrip\-Anetworkdiagnostictool.SHSYNOPSIS\fBtrip\fR[\fB\-c\fR|\fB\-\-config\-file\fR][\fB\-m\fR|\fB\-\-mode\fR][\fB\-u\fR|\fB\-\-unprivileged\fR][\fB\-p\fR|\fB\-\-protocol\fR][\fB\-\-udp\fR][\fB\-\-tcp\fR][\fB\-\-icmp\fR][\fB\-F\fR|\fB\-\-addr\-family\fR][\fB\-4\fR|\fB\-\-ipv4\fR][\fB\-6\fR|\fB\-\-ipv6\fR][\fB\-P\fR|\fB\-\-target\-port\fR][\fB\-S\fR|\fB\-\-source\-port\fR][\fB\-A\fR|\fB\-\-source\-address\fR][\fB\-I\fR|\fB\-\-interface\fR][\fB\-i\fR|\fB\-\-min\-round\-duration\fR][\fB\-T\fR|\fB\-\-max\-round\-duration\fR][\fB\-g\fR|\fB\-\-grace\-duration\fR][\fB\-\-initial\-sequence\fR][\fB\-R\fR|\fB\-\-multipath\-strategy\fR][\fB\-U\fR|\fB\-\-max\-inflight\fR][\fB\-f\fR|\fB\-\-first\-ttl\fR][\fB\-t\fR|\fB\-\-max\-ttl\fR][\fB\-\-packet\-size\fR][\fB\-\-payload\-pattern\fR][\fB\-Q\fR|\fB\-\-tos\fR][\fB\-e\fR|\fB\-\-icmp\-extensions\fR][\fB\-\-read\-timeout\fR][\fB\-r\fR|\fB\-\-dns\-resolve\-method\fR][\fB\-y\fR|\fB\-\-dns\-resolve\-all\fR][\fB\-\-dns\-timeout\fR][\fB\-\-dns\-ttl\fR][\fB\-z\fR|\fB\-\-dns\-lookup\-as\-info\fR][\fB\-s\fR|\fB\-\-max\-samples\fR][\fB\-\-max\-flows\fR][\fB\-a\fR|\fB\-\-tui\-address\-mode\fR][\fB\-\-tui\-as\-mode\fR][\fB\-\-tui\-custom\-columns\fR][\fB\-\-tui\-icmp\-extension\-mode\fR][\fB\-\-tui\-geoip\-mode\fR][\fB\-M\fR|\fB\-\-tui\-max\-addrs\fR][\fB\-\-tui\-preserve\-screen\fR][\fB\-\-tui\-refresh\-rate\fR][\fB\-\-tui\-privacy\-max\-ttl\fR][\fB\-\-tui\-theme\-colors\fR][\fB\-\-print\-tui\-theme\-items\fR][\fB\-\-tui\-key\-bindings\fR][\fB\-\-print\-tui\-binding\-commands\fR][\fB\-C\fR|\fB\-\-report\-cycles\fR][\fB\-G\fR|\fB\-\-geoip\-mmdb\-file\fR][\fB\-\-generate\fR][\fB\-\-generate\-man\fR][\fB\-\-print\-config\-template\fR][\fB\-\-log\-format\fR][\fB\-\-log\-filter\fR][\fB\-\-log\-span\-events\fR][\fB\-v\fR|\fB\-\-verbose\fR][\fB\-h\fR|\fB\-\-help\fR][\fB\-V\fR|\fB\-\-version\fR][\fITARGETS\fR].SHDESCRIPTIONAnetworkdiagnostictool.SHOPTIONS.TP\fB\-c\fR,\fB\-\-config\-file\fR=\fICONFIG_FILE\fRConfigfile.TP\fB\-m\fR,\fB\-\-mode\fR=\fIMODE\fROutputmode[default:tui].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2tui:DisplayinteractiveTUI.IP\(bu2stream:Displayacontinuousstreamoftracingdata.IP\(bu2pretty:GenerateaprettytexttablereportforNcycles.IP\(bu2markdown:GenerateaMarkdowntexttablereportforNcycles.IP\(bu2csv:GenerateaCSVreportforNcycles.IP\(bu2json:GenerateaJSONreportforNcycles.IP\(bu2dot:GenerateaGraphvizDOTfileforNcycles.IP\(bu2flows:DisplayallflowsforNcycles.IP\(bu2silent:DonotgenerateanytracingoutputforNcycles.RE.TP\fB\-u\fR,\fB\-\-unprivileged\fRTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false].TP\fB\-p\fR,\fB\-\-protocol\fR=\fIPROTOCOL\fRTracingprotocol[default:icmp].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2icmp:InternetControlMessageProtocol.IP\(bu2udp:UserDatagramProtocol.IP\(bu2tcp:TransmissionControlProtocol.RE.TP\fB\-\-udp\fRTraceusingtheUDPprotocol.TP\fB\-\-tcp\fRTraceusingtheTCPprotocol.TP\fB\-\-icmp\fRTraceusingtheICMPprotocol.TP\fB\-F\fR,\fB\-\-addr\-family\fR=\fIADDR_FAMILY\fRTheaddressfamily[default:Ipv4thenIpv6].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2ipv4:Ipv4only.IP\(bu2ipv6:Ipv6only.IP\(bu2ipv6\-then\-ipv4:Ipv6withafallbacktoIpv4.IP\(bu2ipv4\-then\-ipv6:Ipv4withafallbacktoIpv6.RE.TP\fB\-4\fR,\fB\-\-ipv4\fRUseIPv4only.TP\fB\-6\fR,\fB\-\-ipv6\fRUseIPv6only.TP\fB\-P\fR,\fB\-\-target\-port\fR=\fITARGET_PORT\fRThetargetport(TCP&UDPonly)[default:80].TP\fB\-S\fR,\fB\-\-source\-port\fR=\fISOURCE_PORT\fRThesourceport(TCP&UDPonly)[default:auto].TP\fB\-A\fR,\fB\-\-source\-address\fR=\fISOURCE_ADDRESS\fRThesourceIPaddress[default:auto].TP\fB\-I\fR,\fB\-\-interface\fR=\fIINTERFACE\fRThenetworkinterface[default:auto].TP\fB\-i\fR,\fB\-\-min\-round\-duration\fR=\fIMIN_ROUND_DURATION\fRTheminimumdurationofeveryround[default:1s].TP\fB\-T\fR,\fB\-\-max\-round\-duration\fR=\fIMAX_ROUND_DURATION\fRThemaximumdurationofeveryround[default:1s].TP\fB\-g\fR,\fB\-\-grace\-duration\fR=\fIGRACE_DURATION\fRTheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms].TP\fB\-\-initial\-sequence\fR=\fIINITIAL_SEQUENCE\fRTheinitialsequencenumber[default:33434].TP\fB\-R\fR,\fB\-\-multipath\-strategy\fR=\fIMULTIPATH_STRATEGY\fRTheEqual\-costMulti\-Pathroutingstrategy(UDPonly)[default:classic].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2classic:Thesrcordestportisusedtostorethesequencenumber.IP\(bu2paris:TheUDP`checksum`fieldisusedtostorethesequencenumber.IP\(bu2dublin:TheIP`identifier`fieldisusedtostorethesequencenumber.RE.TP\fB\-U\fR,\fB\-\-max\-inflight\fR=\fIMAX_INFLIGHT\fRThemaximumnumberofin\-flightICMPechorequests[default:24].TP\fB\-f\fR,\fB\-\-first\-ttl\fR=\fIFIRST_TTL\fRTheTTLtostartfrom[default:1].TP\fB\-t\fR,\fB\-\-max\-ttl\fR=\fIMAX_TTL\fRThemaximumnumberofTTLhops[default:64].TP\fB\-\-packet\-size\fR=\fIPACKET_SIZE\fRThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84].TP\fB\-\-payload\-pattern\fR=\fIPAYLOAD_PATTERN\fRTherepeatingpatterninthepayloadoftheICMPpacket[default:0].TP\fB\-Q\fR,\fB\-\-tos\fR=\fITOS\fRTheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0].TP\fB\-e\fR,\fB\-\-icmp\-extensions\fRParseICMPextensions.TP\fB\-\-read\-timeout\fR=\fIREAD_TIMEOUT\fRThesocketreadtimeout[default:10ms].TP\fB\-r\fR,\fB\-\-dns\-resolve\-method\fR=\fIDNS_RESOLVE_METHOD\fRHowtoperformDNSqueries[default:system].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2system:ResolveusingtheOSresolver.IP\(bu2resolv:Resolveusingthe`/etc/resolv.conf`DNSconfiguration.IP\(bu2google:ResolveusingtheGoogle`8.8.8.8`DNSservice.IP\(bu2cloudflare:ResolveusingtheCloudflare`1.1.1.1`DNSservice.RE.TP\fB\-y\fR,\fB\-\-dns\-resolve\-all\fRTracetoallIPsresolvedfromDNSlookup[default:false].TP\fB\-\-dns\-timeout\fR=\fIDNS_TIMEOUT\fRThemaximumtimetowaittoperformDNSqueries[default:5s].TP\fB\-\-dns\-ttl\fR=\fIDNS_TTL\fRThetime\-to\-live(TTL)ofDNSentries[default:300s].TP\fB\-z\fR,\fB\-\-dns\-lookup\-as\-info\fRLookupautonomoussystem(AS)informationduringDNSqueries[default:false].TP\fB\-s\fR,\fB\-\-max\-samples\fR=\fIMAX_SAMPLES\fRThemaximumnumberofsamplestorecordperhop[default:256].TP\fB\-\-max\-flows\fR=\fIMAX_FLOWS\fRThemaximumnumberofflowstorecord[default:64].TP\fB\-a\fR,\fB\-\-tui\-address\-mode\fR=\fITUI_ADDRESS_MODE\fRHowtorenderaddresses[default:host].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2ip:ShowIPaddressonly.IP\(bu2host:Showreverse\-lookupDNShostnameonly.IP\(bu2both:ShowbothIPaddressandreverse\-lookupDNShostname.RE.TP\fB\-\-tui\-as\-mode\fR=\fITUI_AS_MODE\fRHowtorenderASinformation[default:asn].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2asn:ShowtheASN.IP\(bu2prefix:DisplaytheASprefix.IP\(bu2country\-code:Displaythecountrycode.IP\(bu2registry:Displaytheregistryname.IP\(bu2allocated:Displaytheallocateddate.IP\(bu2name:DisplaytheASname.RE.TP\fB\-\-tui\-custom\-columns\fR=\fITUI_CUSTOM_COLUMNS\fRCustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt].TP\fB\-\-tui\-icmp\-extension\-mode\fR=\fITUI_ICMP_EXTENSION_MODE\fRHowtorenderICMPextensions[default:off].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2off:Donotshow`icmp`extensions.IP\(bu2mpls:ShowMPLSlabel(s)only.IP\(bu2full:Showfull`icmp`extensiondataforallknownextensions.IP\(bu2all:Showfull`icmp`extensiondataforallclasses.RE.TP\fB\-\-tui\-geoip\-mode\fR=\fITUI_GEOIP_MODE\fRHowtorenderGeoIpinformation[default:short].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2off:DonotdisplayGeoIpdata.IP\(bu2short:Showshortformat.IP\(bu2long:Showlongformat.IP\(bu2location:ShowlatitudeandLongitudeformat.RE.TP\fB\-M\fR,\fB\-\-tui\-max\-addrs\fR=\fITUI_MAX_ADDRS\fRThemaximumnumberofaddressestoshowperhop[default:auto].TP\fB\-\-tui\-preserve\-screen\fRPreservethescreenonexit[default:false].TP\fB\-\-tui\-refresh\-rate\fR=\fITUI_REFRESH_RATE\fRTheTuirefreshrate[default:100ms].TP\fB\-\-tui\-privacy\-max\-ttl\fR=\fITUI_PRIVACY_MAX_TTL\fRThemaximumttlofhopswhichwillbemaskedforprivacy[default:0].TP\fB\-\-tui\-theme\-colors\fR=\fITUI_THEME_COLORS\fRTheTUIthemecolors[item=color,item=color,..].TP\fB\-\-print\-tui\-theme\-items\fRPrintallTUIthemeitemsandexit.TP\fB\-\-tui\-key\-bindings\fR=\fITUI_KEY_BINDINGS\fRTheTUIkeybindings[command=key,command=key,..].TP\fB\-\-print\-tui\-binding\-commands\fRPrintallTUIcommandsthatcanbeboundandexit.TP\fB\-C\fR,\fB\-\-report\-cycles\fR=\fIREPORT_CYCLES\fRThenumberofreportcyclestorun[default:10].TP\fB\-G\fR,\fB\-\-geoip\-mmdb\-file\fR=\fIGEOIP_MMDB_FILE\fRThesupportedMaxMindorIPinfoGeoIpmmdbfile.TP\fB\-\-generate\fR=\fIGENERATE\fRGenerateshellcompletion.br.br[\fIpossiblevalues:\fRbash,elvish,fish,powershell,zsh].TP\fB\-\-generate\-man\fRGenerateROFFmanpage.TP\fB\-\-print\-config\-template\fRPrintatemplatetomlconfigfileandexit.TP\fB\-\-log\-format\fR=\fILOG_FORMAT\fRThedebuglogformat[default:pretty].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2compact:Displaylogdatainacompactformat.IP\(bu2pretty:Displaylogdatainaprettyformat.IP\(bu2json:Displaylogdatainajsonformat.IP\(bu2chrome:DisplaylogdatainChrometraceformat.RE.TP\fB\-\-log\-filter\fR=\fILOG_FILTER\fRThedebuglogfilter[default:trippy=debug].TP\fB\-\-log\-span\-events\fR=\fILOG_SPAN_EVENTS\fRThedebuglogformat[default:off].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2off:Donotdisplayeventspans.IP\(bu2active:Displayenterandexiteventspans.IP\(bu2full:Displayalleventspans.RE.TP\fB\-v\fR,\fB\-\-verbose\fREnableverbosedebuglogging.TP\fB\-h\fR,\fB\-\-help\fRPrinthelp(seeasummarywith\*(Aq\-h\*(Aq).TP\fB\-V\fR,\fB\-\-version\fRPrintversion.TP[\fITARGETS\fR]AspacedelimitedlistofhostnamesandIPstotrace.SHVERSIONv0.12.0\-dev.SHAUTHORSFujiApple<fujiapple852@gmail.com>
+.ie\n(.g.dsAq\(aq.el.dsAq'.THtrip1"trip0.12.0-dev".SHNAMEtrip\-Anetworkdiagnostictool.SHSYNOPSIS\fBtrip\fR[\fB\-c\fR|\fB\-\-config\-file\fR][\fB\-m\fR|\fB\-\-mode\fR][\fB\-u\fR|\fB\-\-unprivileged\fR][\fB\-p\fR|\fB\-\-protocol\fR][\fB\-\-udp\fR][\fB\-\-tcp\fR][\fB\-\-icmp\fR][\fB\-F\fR|\fB\-\-addr\-family\fR][\fB\-4\fR|\fB\-\-ipv4\fR][\fB\-6\fR|\fB\-\-ipv6\fR][\fB\-P\fR|\fB\-\-target\-port\fR][\fB\-S\fR|\fB\-\-source\-port\fR][\fB\-A\fR|\fB\-\-source\-address\fR][\fB\-I\fR|\fB\-\-interface\fR][\fB\-i\fR|\fB\-\-min\-round\-duration\fR][\fB\-T\fR|\fB\-\-max\-round\-duration\fR][\fB\-g\fR|\fB\-\-grace\-duration\fR][\fB\-\-initial\-sequence\fR][\fB\-R\fR|\fB\-\-multipath\-strategy\fR][\fB\-U\fR|\fB\-\-max\-inflight\fR][\fB\-f\fR|\fB\-\-first\-ttl\fR][\fB\-t\fR|\fB\-\-max\-ttl\fR][\fB\-\-packet\-size\fR][\fB\-\-payload\-pattern\fR][\fB\-Q\fR|\fB\-\-tos\fR][\fB\-e\fR|\fB\-\-icmp\-extensions\fR][\fB\-\-read\-timeout\fR][\fB\-r\fR|\fB\-\-dns\-resolve\-method\fR][\fB\-y\fR|\fB\-\-dns\-resolve\-all\fR][\fB\-\-dns\-timeout\fR][\fB\-\-dns\-ttl\fR][\fB\-z\fR|\fB\-\-dns\-lookup\-as\-info\fR][\fB\-s\fR|\fB\-\-max\-samples\fR][\fB\-\-max\-flows\fR][\fB\-a\fR|\fB\-\-tui\-address\-mode\fR][\fB\-\-tui\-as\-mode\fR][\fB\-\-tui\-custom\-columns\fR][\fB\-\-tui\-icmp\-extension\-mode\fR][\fB\-\-tui\-geoip\-mode\fR][\fB\-M\fR|\fB\-\-tui\-max\-addrs\fR][\fB\-\-tui\-preserve\-screen\fR][\fB\-\-tui\-refresh\-rate\fR][\fB\-\-tui\-privacy\-max\-ttl\fR][\fB\-\-tui\-locale\fR][\fB\-\-tui\-theme\-colors\fR][\fB\-\-print\-tui\-theme\-items\fR][\fB\-\-tui\-key\-bindings\fR][\fB\-\-print\-tui\-binding\-commands\fR][\fB\-C\fR|\fB\-\-report\-cycles\fR][\fB\-G\fR|\fB\-\-geoip\-mmdb\-file\fR][\fB\-\-generate\fR][\fB\-\-generate\-man\fR][\fB\-\-print\-config\-template\fR][\fB\-\-log\-format\fR][\fB\-\-log\-filter\fR][\fB\-\-log\-span\-events\fR][\fB\-v\fR|\fB\-\-verbose\fR][\fB\-h\fR|\fB\-\-help\fR][\fB\-V\fR|\fB\-\-version\fR][\fITARGETS\fR].SHDESCRIPTIONAnetworkdiagnostictool.SHOPTIONS.TP\fB\-c\fR,\fB\-\-config\-file\fR=\fICONFIG_FILE\fRConfigfile.TP\fB\-m\fR,\fB\-\-mode\fR=\fIMODE\fROutputmode[default:tui].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2tui:DisplayinteractiveTUI.IP\(bu2stream:Displayacontinuousstreamoftracingdata.IP\(bu2pretty:GenerateaprettytexttablereportforNcycles.IP\(bu2markdown:GenerateaMarkdowntexttablereportforNcycles.IP\(bu2csv:GenerateaCSVreportforNcycles.IP\(bu2json:GenerateaJSONreportforNcycles.IP\(bu2dot:GenerateaGraphvizDOTfileforNcycles.IP\(bu2flows:DisplayallflowsforNcycles.IP\(bu2silent:DonotgenerateanytracingoutputforNcycles.RE.TP\fB\-u\fR,\fB\-\-unprivileged\fRTracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false].TP\fB\-p\fR,\fB\-\-protocol\fR=\fIPROTOCOL\fRTracingprotocol[default:icmp].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2icmp:InternetControlMessageProtocol.IP\(bu2udp:UserDatagramProtocol.IP\(bu2tcp:TransmissionControlProtocol.RE.TP\fB\-\-udp\fRTraceusingtheUDPprotocol.TP\fB\-\-tcp\fRTraceusingtheTCPprotocol.TP\fB\-\-icmp\fRTraceusingtheICMPprotocol.TP\fB\-F\fR,\fB\-\-addr\-family\fR=\fIADDR_FAMILY\fRTheaddressfamily[default:Ipv4thenIpv6].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2ipv4:Ipv4only.IP\(bu2ipv6:Ipv6only.IP\(bu2ipv6\-then\-ipv4:Ipv6withafallbacktoIpv4.IP\(bu2ipv4\-then\-ipv6:Ipv4withafallbacktoIpv6.RE.TP\fB\-4\fR,\fB\-\-ipv4\fRUseIPv4only.TP\fB\-6\fR,\fB\-\-ipv6\fRUseIPv6only.TP\fB\-P\fR,\fB\-\-target\-port\fR=\fITARGET_PORT\fRThetargetport(TCP&UDPonly)[default:80].TP\fB\-S\fR,\fB\-\-source\-port\fR=\fISOURCE_PORT\fRThesourceport(TCP&UDPonly)[default:auto].TP\fB\-A\fR,\fB\-\-source\-address\fR=\fISOURCE_ADDRESS\fRThesourceIPaddress[default:auto].TP\fB\-I\fR,\fB\-\-interface\fR=\fIINTERFACE\fRThenetworkinterface[default:auto].TP\fB\-i\fR,\fB\-\-min\-round\-duration\fR=\fIMIN_ROUND_DURATION\fRTheminimumdurationofeveryround[default:1s].TP\fB\-T\fR,\fB\-\-max\-round\-duration\fR=\fIMAX_ROUND_DURATION\fRThemaximumdurationofeveryround[default:1s].TP\fB\-g\fR,\fB\-\-grace\-duration\fR=\fIGRACE_DURATION\fRTheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms].TP\fB\-\-initial\-sequence\fR=\fIINITIAL_SEQUENCE\fRTheinitialsequencenumber[default:33434].TP\fB\-R\fR,\fB\-\-multipath\-strategy\fR=\fIMULTIPATH_STRATEGY\fRTheEqual\-costMulti\-Pathroutingstrategy(UDPonly)[default:classic].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2classic:Thesrcordestportisusedtostorethesequencenumber.IP\(bu2paris:TheUDP`checksum`fieldisusedtostorethesequencenumber.IP\(bu2dublin:TheIP`identifier`fieldisusedtostorethesequencenumber.RE.TP\fB\-U\fR,\fB\-\-max\-inflight\fR=\fIMAX_INFLIGHT\fRThemaximumnumberofin\-flightICMPechorequests[default:24].TP\fB\-f\fR,\fB\-\-first\-ttl\fR=\fIFIRST_TTL\fRTheTTLtostartfrom[default:1].TP\fB\-t\fR,\fB\-\-max\-ttl\fR=\fIMAX_TTL\fRThemaximumnumberofTTLhops[default:64].TP\fB\-\-packet\-size\fR=\fIPACKET_SIZE\fRThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84].TP\fB\-\-payload\-pattern\fR=\fIPAYLOAD_PATTERN\fRTherepeatingpatterninthepayloadoftheICMPpacket[default:0].TP\fB\-Q\fR,\fB\-\-tos\fR=\fITOS\fRTheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0].TP\fB\-e\fR,\fB\-\-icmp\-extensions\fRParseICMPextensions.TP\fB\-\-read\-timeout\fR=\fIREAD_TIMEOUT\fRThesocketreadtimeout[default:10ms].TP\fB\-r\fR,\fB\-\-dns\-resolve\-method\fR=\fIDNS_RESOLVE_METHOD\fRHowtoperformDNSqueries[default:system].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2system:ResolveusingtheOSresolver.IP\(bu2resolv:Resolveusingthe`/etc/resolv.conf`DNSconfiguration.IP\(bu2google:ResolveusingtheGoogle`8.8.8.8`DNSservice.IP\(bu2cloudflare:ResolveusingtheCloudflare`1.1.1.1`DNSservice.RE.TP\fB\-y\fR,\fB\-\-dns\-resolve\-all\fRTracetoallIPsresolvedfromDNSlookup[default:false].TP\fB\-\-dns\-timeout\fR=\fIDNS_TIMEOUT\fRThemaximumtimetowaittoperformDNSqueries[default:5s].TP\fB\-\-dns\-ttl\fR=\fIDNS_TTL\fRThetime\-to\-live(TTL)ofDNSentries[default:300s].TP\fB\-z\fR,\fB\-\-dns\-lookup\-as\-info\fRLookupautonomoussystem(AS)informationduringDNSqueries[default:false].TP\fB\-s\fR,\fB\-\-max\-samples\fR=\fIMAX_SAMPLES\fRThemaximumnumberofsamplestorecordperhop[default:256].TP\fB\-\-max\-flows\fR=\fIMAX_FLOWS\fRThemaximumnumberofflowstorecord[default:64].TP\fB\-a\fR,\fB\-\-tui\-address\-mode\fR=\fITUI_ADDRESS_MODE\fRHowtorenderaddresses[default:host].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2ip:ShowIPaddressonly.IP\(bu2host:Showreverse\-lookupDNShostnameonly.IP\(bu2both:ShowbothIPaddressandreverse\-lookupDNShostname.RE.TP\fB\-\-tui\-as\-mode\fR=\fITUI_AS_MODE\fRHowtorenderASinformation[default:asn].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2asn:ShowtheASN.IP\(bu2prefix:DisplaytheASprefix.IP\(bu2country\-code:Displaythecountrycode.IP\(bu2registry:Displaytheregistryname.IP\(bu2allocated:Displaytheallocateddate.IP\(bu2name:DisplaytheASname.RE.TP\fB\-\-tui\-custom\-columns\fR=\fITUI_CUSTOM_COLUMNS\fRCustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt].TP\fB\-\-tui\-icmp\-extension\-mode\fR=\fITUI_ICMP_EXTENSION_MODE\fRHowtorenderICMPextensions[default:off].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2off:Donotshow`icmp`extensions.IP\(bu2mpls:ShowMPLSlabel(s)only.IP\(bu2full:Showfull`icmp`extensiondataforallknownextensions.IP\(bu2all:Showfull`icmp`extensiondataforallclasses.RE.TP\fB\-\-tui\-geoip\-mode\fR=\fITUI_GEOIP_MODE\fRHowtorenderGeoIpinformation[default:short].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2off:DonotdisplayGeoIpdata.IP\(bu2short:Showshortformat.IP\(bu2long:Showlongformat.IP\(bu2location:ShowlatitudeandLongitudeformat.RE.TP\fB\-M\fR,\fB\-\-tui\-max\-addrs\fR=\fITUI_MAX_ADDRS\fRThemaximumnumberofaddressestoshowperhop[default:auto].TP\fB\-\-tui\-preserve\-screen\fRPreservethescreenonexit[default:false].TP\fB\-\-tui\-refresh\-rate\fR=\fITUI_REFRESH_RATE\fRTheTuirefreshrate[default:100ms].TP\fB\-\-tui\-privacy\-max\-ttl\fR=\fITUI_PRIVACY_MAX_TTL\fRThemaximumttlofhopswhichwillbemaskedforprivacy[default:0].TP\fB\-\-tui\-locale\fR=\fITUI_LOCALE\fRThelocaletousefortheTUI[default:auto].TP\fB\-\-tui\-theme\-colors\fR=\fITUI_THEME_COLORS\fRTheTUIthemecolors[item=color,item=color,..].TP\fB\-\-print\-tui\-theme\-items\fRPrintallTUIthemeitemsandexit.TP\fB\-\-tui\-key\-bindings\fR=\fITUI_KEY_BINDINGS\fRTheTUIkeybindings[command=key,command=key,..].TP\fB\-\-print\-tui\-binding\-commands\fRPrintallTUIcommandsthatcanbeboundandexit.TP\fB\-C\fR,\fB\-\-report\-cycles\fR=\fIREPORT_CYCLES\fRThenumberofreportcyclestorun[default:10].TP\fB\-G\fR,\fB\-\-geoip\-mmdb\-file\fR=\fIGEOIP_MMDB_FILE\fRThesupportedMaxMindorIPinfoGeoIpmmdbfile.TP\fB\-\-generate\fR=\fIGENERATE\fRGenerateshellcompletion.br.br[\fIpossiblevalues:\fRbash,elvish,fish,powershell,zsh].TP\fB\-\-generate\-man\fRGenerateROFFmanpage.TP\fB\-\-print\-config\-template\fRPrintatemplatetomlconfigfileandexit.TP\fB\-\-log\-format\fR=\fILOG_FORMAT\fRThedebuglogformat[default:pretty].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2compact:Displaylogdatainacompactformat.IP\(bu2pretty:Displaylogdatainaprettyformat.IP\(bu2json:Displaylogdatainajsonformat.IP\(bu2chrome:DisplaylogdatainChrometraceformat.RE.TP\fB\-\-log\-filter\fR=\fILOG_FILTER\fRThedebuglogfilter[default:trippy=debug].TP\fB\-\-log\-span\-events\fR=\fILOG_SPAN_EVENTS\fRThedebuglogformat[default:off].br.br\fIPossiblevalues:\fR.RS14.IP\(bu2off:Donotdisplayeventspans.IP\(bu2active:Displayenterandexiteventspans.IP\(bu2full:Displayalleventspans.RE.TP\fB\-v\fR,\fB\-\-verbose\fREnableverbosedebuglogging.TP\fB\-h\fR,\fB\-\-help\fRPrinthelp(seeasummarywith\*(Aq\-h\*(Aq).TP\fB\-V\fR,\fB\-\-version\fRPrintversion.TP[\fITARGETS\fR]AspacedelimitedlistofhostnamesandIPstotrace.SHVERSIONv0.12.0\-dev.SHAUTHORSFujiApple<fujiapple852@gmail.com>
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_powershell_shell_completions.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_powershell_shell_completions.snap
index 4abfc9623..4b78da224 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_powershell_shell_completions.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_powershell_shell_completions.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/print.rs
 ---
-usingnamespaceSystem.Management.AutomationusingnamespaceSystem.Management.Automation.LanguageRegister-ArgumentCompleter-Native-CommandName'trip'-ScriptBlock{param($wordToComplete,$commandAst,$cursorPosition)$commandElements=$commandAst.CommandElements$command=@('trip'for($i=1;$i-lt$commandElements.Count;$i++){$element=$commandElements[$i]if($element-isnot[StringConstantExpressionAst]-or$element.StringConstantType-ne[StringConstantType]::BareWord-or$element.Value.StartsWith('-')-or$element.Value-eq$wordToComplete){break}$element.Value})-join';'$completions=@(switch($command){'trip'{[CompletionResult]::new('-c','-c',[CompletionResultType]::ParameterName,'Configfile')[CompletionResult]::new('--config-file','--config-file',[CompletionResultType]::ParameterName,'Configfile')[CompletionResult]::new('-m','-m',[CompletionResultType]::ParameterName,'Outputmode[default:tui]')[CompletionResult]::new('--mode','--mode',[CompletionResultType]::ParameterName,'Outputmode[default:tui]')[CompletionResult]::new('-p','-p',[CompletionResultType]::ParameterName,'Tracingprotocol[default:icmp]')[CompletionResult]::new('--protocol','--protocol',[CompletionResultType]::ParameterName,'Tracingprotocol[default:icmp]')[CompletionResult]::new('-F','-F',[CompletionResultType]::ParameterName,'Theaddressfamily[default:Ipv4thenIpv6]')[CompletionResult]::new('--addr-family','--addr-family',[CompletionResultType]::ParameterName,'Theaddressfamily[default:Ipv4thenIpv6]')[CompletionResult]::new('-P','-P',[CompletionResultType]::ParameterName,'Thetargetport(TCP&UDPonly)[default:80]')[CompletionResult]::new('--target-port','--target-port',[CompletionResultType]::ParameterName,'Thetargetport(TCP&UDPonly)[default:80]')[CompletionResult]::new('-S','-S',[CompletionResultType]::ParameterName,'Thesourceport(TCP&UDPonly)[default:auto]')[CompletionResult]::new('--source-port','--source-port',[CompletionResultType]::ParameterName,'Thesourceport(TCP&UDPonly)[default:auto]')[CompletionResult]::new('-A','-A',[CompletionResultType]::ParameterName,'ThesourceIPaddress[default:auto]')[CompletionResult]::new('--source-address','--source-address',[CompletionResultType]::ParameterName,'ThesourceIPaddress[default:auto]')[CompletionResult]::new('-I','-I',[CompletionResultType]::ParameterName,'Thenetworkinterface[default:auto]')[CompletionResult]::new('--interface','--interface',[CompletionResultType]::ParameterName,'Thenetworkinterface[default:auto]')[CompletionResult]::new('-i','-i',[CompletionResultType]::ParameterName,'Theminimumdurationofeveryround[default:1s]')[CompletionResult]::new('--min-round-duration','--min-round-duration',[CompletionResultType]::ParameterName,'Theminimumdurationofeveryround[default:1s]')[CompletionResult]::new('-T','-T',[CompletionResultType]::ParameterName,'Themaximumdurationofeveryround[default:1s]')[CompletionResult]::new('--max-round-duration','--max-round-duration',[CompletionResultType]::ParameterName,'Themaximumdurationofeveryround[default:1s]')[CompletionResult]::new('-g','-g',[CompletionResultType]::ParameterName,'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]')[CompletionResult]::new('--grace-duration','--grace-duration',[CompletionResultType]::ParameterName,'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]')[CompletionResult]::new('--initial-sequence','--initial-sequence',[CompletionResultType]::ParameterName,'Theinitialsequencenumber[default:33434]')[CompletionResult]::new('-R','-R',[CompletionResultType]::ParameterName,'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]')[CompletionResult]::new('--multipath-strategy','--multipath-strategy',[CompletionResultType]::ParameterName,'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]')[CompletionResult]::new('-U','-U',[CompletionResultType]::ParameterName,'Themaximumnumberofin-flightICMPechorequests[default:24]')[CompletionResult]::new('--max-inflight','--max-inflight',[CompletionResultType]::ParameterName,'Themaximumnumberofin-flightICMPechorequests[default:24]')[CompletionResult]::new('-f','-f',[CompletionResultType]::ParameterName,'TheTTLtostartfrom[default:1]')[CompletionResult]::new('--first-ttl','--first-ttl',[CompletionResultType]::ParameterName,'TheTTLtostartfrom[default:1]')[CompletionResult]::new('-t','-t',[CompletionResultType]::ParameterName,'ThemaximumnumberofTTLhops[default:64]')[CompletionResult]::new('--max-ttl','--max-ttl',[CompletionResultType]::ParameterName,'ThemaximumnumberofTTLhops[default:64]')[CompletionResult]::new('--packet-size','--packet-size',[CompletionResultType]::ParameterName,'ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]')[CompletionResult]::new('--payload-pattern','--payload-pattern',[CompletionResultType]::ParameterName,'TherepeatingpatterninthepayloadoftheICMPpacket[default:0]')[CompletionResult]::new('-Q','-Q',[CompletionResultType]::ParameterName,'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]')[CompletionResult]::new('--tos','--tos',[CompletionResultType]::ParameterName,'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]')[CompletionResult]::new('--read-timeout','--read-timeout',[CompletionResultType]::ParameterName,'Thesocketreadtimeout[default:10ms]')[CompletionResult]::new('-r','-r',[CompletionResultType]::ParameterName,'HowtoperformDNSqueries[default:system]')[CompletionResult]::new('--dns-resolve-method','--dns-resolve-method',[CompletionResultType]::ParameterName,'HowtoperformDNSqueries[default:system]')[CompletionResult]::new('--dns-timeout','--dns-timeout',[CompletionResultType]::ParameterName,'ThemaximumtimetowaittoperformDNSqueries[default:5s]')[CompletionResult]::new('--dns-ttl','--dns-ttl',[CompletionResultType]::ParameterName,'Thetime-to-live(TTL)ofDNSentries[default:300s]')[CompletionResult]::new('-s','-s',[CompletionResultType]::ParameterName,'Themaximumnumberofsamplestorecordperhop[default:256]')[CompletionResult]::new('--max-samples','--max-samples',[CompletionResultType]::ParameterName,'Themaximumnumberofsamplestorecordperhop[default:256]')[CompletionResult]::new('--max-flows','--max-flows',[CompletionResultType]::ParameterName,'Themaximumnumberofflowstorecord[default:64]')[CompletionResult]::new('-a','-a',[CompletionResultType]::ParameterName,'Howtorenderaddresses[default:host]')[CompletionResult]::new('--tui-address-mode','--tui-address-mode',[CompletionResultType]::ParameterName,'Howtorenderaddresses[default:host]')[CompletionResult]::new('--tui-as-mode','--tui-as-mode',[CompletionResultType]::ParameterName,'HowtorenderASinformation[default:asn]')[CompletionResult]::new('--tui-custom-columns','--tui-custom-columns',[CompletionResultType]::ParameterName,'CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]')[CompletionResult]::new('--tui-icmp-extension-mode','--tui-icmp-extension-mode',[CompletionResultType]::ParameterName,'HowtorenderICMPextensions[default:off]')[CompletionResult]::new('--tui-geoip-mode','--tui-geoip-mode',[CompletionResultType]::ParameterName,'HowtorenderGeoIpinformation[default:short]')[CompletionResult]::new('-M','-M',[CompletionResultType]::ParameterName,'Themaximumnumberofaddressestoshowperhop[default:auto]')[CompletionResult]::new('--tui-max-addrs','--tui-max-addrs',[CompletionResultType]::ParameterName,'Themaximumnumberofaddressestoshowperhop[default:auto]')[CompletionResult]::new('--tui-refresh-rate','--tui-refresh-rate',[CompletionResultType]::ParameterName,'TheTuirefreshrate[default:100ms]')[CompletionResult]::new('--tui-privacy-max-ttl','--tui-privacy-max-ttl',[CompletionResultType]::ParameterName,'Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]')[CompletionResult]::new('--tui-theme-colors','--tui-theme-colors',[CompletionResultType]::ParameterName,'TheTUIthemecolors[item=color,item=color,..]')[CompletionResult]::new('--tui-key-bindings','--tui-key-bindings',[CompletionResultType]::ParameterName,'TheTUIkeybindings[command=key,command=key,..]')[CompletionResult]::new('-C','-C',[CompletionResultType]::ParameterName,'Thenumberofreportcyclestorun[default:10]')[CompletionResult]::new('--report-cycles','--report-cycles',[CompletionResultType]::ParameterName,'Thenumberofreportcyclestorun[default:10]')[CompletionResult]::new('-G','-G',[CompletionResultType]::ParameterName,'ThesupportedMaxMindorIPinfoGeoIpmmdbfile')[CompletionResult]::new('--geoip-mmdb-file','--geoip-mmdb-file',[CompletionResultType]::ParameterName,'ThesupportedMaxMindorIPinfoGeoIpmmdbfile')[CompletionResult]::new('--generate','--generate',[CompletionResultType]::ParameterName,'Generateshellcompletion')[CompletionResult]::new('--log-format','--log-format',[CompletionResultType]::ParameterName,'Thedebuglogformat[default:pretty]')[CompletionResult]::new('--log-filter','--log-filter',[CompletionResultType]::ParameterName,'Thedebuglogfilter[default:trippy=debug]')[CompletionResult]::new('--log-span-events','--log-span-events',[CompletionResultType]::ParameterName,'Thedebuglogformat[default:off]')[CompletionResult]::new('-u','-u',[CompletionResultType]::ParameterName,'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]')[CompletionResult]::new('--unprivileged','--unprivileged',[CompletionResultType]::ParameterName,'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]')[CompletionResult]::new('--udp','--udp',[CompletionResultType]::ParameterName,'TraceusingtheUDPprotocol')[CompletionResult]::new('--tcp','--tcp',[CompletionResultType]::ParameterName,'TraceusingtheTCPprotocol')[CompletionResult]::new('--icmp','--icmp',[CompletionResultType]::ParameterName,'TraceusingtheICMPprotocol')[CompletionResult]::new('-4','-4',[CompletionResultType]::ParameterName,'UseIPv4only')[CompletionResult]::new('--ipv4','--ipv4',[CompletionResultType]::ParameterName,'UseIPv4only')[CompletionResult]::new('-6','-6',[CompletionResultType]::ParameterName,'UseIPv6only')[CompletionResult]::new('--ipv6','--ipv6',[CompletionResultType]::ParameterName,'UseIPv6only')[CompletionResult]::new('-e','-e',[CompletionResultType]::ParameterName,'ParseICMPextensions')[CompletionResult]::new('--icmp-extensions','--icmp-extensions',[CompletionResultType]::ParameterName,'ParseICMPextensions')[CompletionResult]::new('-y','-y',[CompletionResultType]::ParameterName,'TracetoallIPsresolvedfromDNSlookup[default:false]')[CompletionResult]::new('--dns-resolve-all','--dns-resolve-all',[CompletionResultType]::ParameterName,'TracetoallIPsresolvedfromDNSlookup[default:false]')[CompletionResult]::new('-z','-z',[CompletionResultType]::ParameterName,'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]')[CompletionResult]::new('--dns-lookup-as-info','--dns-lookup-as-info',[CompletionResultType]::ParameterName,'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]')[CompletionResult]::new('--tui-preserve-screen','--tui-preserve-screen',[CompletionResultType]::ParameterName,'Preservethescreenonexit[default:false]')[CompletionResult]::new('--print-tui-theme-items','--print-tui-theme-items',[CompletionResultType]::ParameterName,'PrintallTUIthemeitemsandexit')[CompletionResult]::new('--print-tui-binding-commands','--print-tui-binding-commands',[CompletionResultType]::ParameterName,'PrintallTUIcommandsthatcanbeboundandexit')[CompletionResult]::new('--generate-man','--generate-man',[CompletionResultType]::ParameterName,'GenerateROFFmanpage')[CompletionResult]::new('--print-config-template','--print-config-template',[CompletionResultType]::ParameterName,'Printatemplatetomlconfigfileandexit')[CompletionResult]::new('-v','-v',[CompletionResultType]::ParameterName,'Enableverbosedebuglogging')[CompletionResult]::new('--verbose','--verbose',[CompletionResultType]::ParameterName,'Enableverbosedebuglogging')[CompletionResult]::new('-h','-h',[CompletionResultType]::ParameterName,'Printhelp(seemorewith''--help'')')[CompletionResult]::new('--help','--help',[CompletionResultType]::ParameterName,'Printhelp(seemorewith''--help'')')[CompletionResult]::new('-V','-V',[CompletionResultType]::ParameterName,'Printversion')[CompletionResult]::new('--version','--version',[CompletionResultType]::ParameterName,'Printversion')break}})$completions.Where{$_.CompletionText-like"$wordToComplete*"}|Sort-Object-PropertyListItemText}
+usingnamespaceSystem.Management.AutomationusingnamespaceSystem.Management.Automation.LanguageRegister-ArgumentCompleter-Native-CommandName'trip'-ScriptBlock{param($wordToComplete,$commandAst,$cursorPosition)$commandElements=$commandAst.CommandElements$command=@('trip'for($i=1;$i-lt$commandElements.Count;$i++){$element=$commandElements[$i]if($element-isnot[StringConstantExpressionAst]-or$element.StringConstantType-ne[StringConstantType]::BareWord-or$element.Value.StartsWith('-')-or$element.Value-eq$wordToComplete){break}$element.Value})-join';'$completions=@(switch($command){'trip'{[CompletionResult]::new('-c','-c',[CompletionResultType]::ParameterName,'Configfile')[CompletionResult]::new('--config-file','--config-file',[CompletionResultType]::ParameterName,'Configfile')[CompletionResult]::new('-m','-m',[CompletionResultType]::ParameterName,'Outputmode[default:tui]')[CompletionResult]::new('--mode','--mode',[CompletionResultType]::ParameterName,'Outputmode[default:tui]')[CompletionResult]::new('-p','-p',[CompletionResultType]::ParameterName,'Tracingprotocol[default:icmp]')[CompletionResult]::new('--protocol','--protocol',[CompletionResultType]::ParameterName,'Tracingprotocol[default:icmp]')[CompletionResult]::new('-F','-F',[CompletionResultType]::ParameterName,'Theaddressfamily[default:Ipv4thenIpv6]')[CompletionResult]::new('--addr-family','--addr-family',[CompletionResultType]::ParameterName,'Theaddressfamily[default:Ipv4thenIpv6]')[CompletionResult]::new('-P','-P',[CompletionResultType]::ParameterName,'Thetargetport(TCP&UDPonly)[default:80]')[CompletionResult]::new('--target-port','--target-port',[CompletionResultType]::ParameterName,'Thetargetport(TCP&UDPonly)[default:80]')[CompletionResult]::new('-S','-S',[CompletionResultType]::ParameterName,'Thesourceport(TCP&UDPonly)[default:auto]')[CompletionResult]::new('--source-port','--source-port',[CompletionResultType]::ParameterName,'Thesourceport(TCP&UDPonly)[default:auto]')[CompletionResult]::new('-A','-A',[CompletionResultType]::ParameterName,'ThesourceIPaddress[default:auto]')[CompletionResult]::new('--source-address','--source-address',[CompletionResultType]::ParameterName,'ThesourceIPaddress[default:auto]')[CompletionResult]::new('-I','-I',[CompletionResultType]::ParameterName,'Thenetworkinterface[default:auto]')[CompletionResult]::new('--interface','--interface',[CompletionResultType]::ParameterName,'Thenetworkinterface[default:auto]')[CompletionResult]::new('-i','-i',[CompletionResultType]::ParameterName,'Theminimumdurationofeveryround[default:1s]')[CompletionResult]::new('--min-round-duration','--min-round-duration',[CompletionResultType]::ParameterName,'Theminimumdurationofeveryround[default:1s]')[CompletionResult]::new('-T','-T',[CompletionResultType]::ParameterName,'Themaximumdurationofeveryround[default:1s]')[CompletionResult]::new('--max-round-duration','--max-round-duration',[CompletionResultType]::ParameterName,'Themaximumdurationofeveryround[default:1s]')[CompletionResult]::new('-g','-g',[CompletionResultType]::ParameterName,'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]')[CompletionResult]::new('--grace-duration','--grace-duration',[CompletionResultType]::ParameterName,'TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded[default:100ms]')[CompletionResult]::new('--initial-sequence','--initial-sequence',[CompletionResultType]::ParameterName,'Theinitialsequencenumber[default:33434]')[CompletionResult]::new('-R','-R',[CompletionResultType]::ParameterName,'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]')[CompletionResult]::new('--multipath-strategy','--multipath-strategy',[CompletionResultType]::ParameterName,'TheEqual-costMulti-Pathroutingstrategy(UDPonly)[default:classic]')[CompletionResult]::new('-U','-U',[CompletionResultType]::ParameterName,'Themaximumnumberofin-flightICMPechorequests[default:24]')[CompletionResult]::new('--max-inflight','--max-inflight',[CompletionResultType]::ParameterName,'Themaximumnumberofin-flightICMPechorequests[default:24]')[CompletionResult]::new('-f','-f',[CompletionResultType]::ParameterName,'TheTTLtostartfrom[default:1]')[CompletionResult]::new('--first-ttl','--first-ttl',[CompletionResultType]::ParameterName,'TheTTLtostartfrom[default:1]')[CompletionResult]::new('-t','-t',[CompletionResultType]::ParameterName,'ThemaximumnumberofTTLhops[default:64]')[CompletionResult]::new('--max-ttl','--max-ttl',[CompletionResultType]::ParameterName,'ThemaximumnumberofTTLhops[default:64]')[CompletionResult]::new('--packet-size','--packet-size',[CompletionResultType]::ParameterName,'ThesizeofIPpackettosend(IPheader+ICMPheader+payload)[default:84]')[CompletionResult]::new('--payload-pattern','--payload-pattern',[CompletionResultType]::ParameterName,'TherepeatingpatterninthepayloadoftheICMPpacket[default:0]')[CompletionResult]::new('-Q','-Q',[CompletionResultType]::ParameterName,'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]')[CompletionResult]::new('--tos','--tos',[CompletionResultType]::ParameterName,'TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)[default:0]')[CompletionResult]::new('--read-timeout','--read-timeout',[CompletionResultType]::ParameterName,'Thesocketreadtimeout[default:10ms]')[CompletionResult]::new('-r','-r',[CompletionResultType]::ParameterName,'HowtoperformDNSqueries[default:system]')[CompletionResult]::new('--dns-resolve-method','--dns-resolve-method',[CompletionResultType]::ParameterName,'HowtoperformDNSqueries[default:system]')[CompletionResult]::new('--dns-timeout','--dns-timeout',[CompletionResultType]::ParameterName,'ThemaximumtimetowaittoperformDNSqueries[default:5s]')[CompletionResult]::new('--dns-ttl','--dns-ttl',[CompletionResultType]::ParameterName,'Thetime-to-live(TTL)ofDNSentries[default:300s]')[CompletionResult]::new('-s','-s',[CompletionResultType]::ParameterName,'Themaximumnumberofsamplestorecordperhop[default:256]')[CompletionResult]::new('--max-samples','--max-samples',[CompletionResultType]::ParameterName,'Themaximumnumberofsamplestorecordperhop[default:256]')[CompletionResult]::new('--max-flows','--max-flows',[CompletionResultType]::ParameterName,'Themaximumnumberofflowstorecord[default:64]')[CompletionResult]::new('-a','-a',[CompletionResultType]::ParameterName,'Howtorenderaddresses[default:host]')[CompletionResult]::new('--tui-address-mode','--tui-address-mode',[CompletionResultType]::ParameterName,'Howtorenderaddresses[default:host]')[CompletionResult]::new('--tui-as-mode','--tui-as-mode',[CompletionResultType]::ParameterName,'HowtorenderASinformation[default:asn]')[CompletionResult]::new('--tui-custom-columns','--tui-custom-columns',[CompletionResultType]::ParameterName,'CustomcolumnstobedisplayedintheTUIhopstable[default:holsravbwdt]')[CompletionResult]::new('--tui-icmp-extension-mode','--tui-icmp-extension-mode',[CompletionResultType]::ParameterName,'HowtorenderICMPextensions[default:off]')[CompletionResult]::new('--tui-geoip-mode','--tui-geoip-mode',[CompletionResultType]::ParameterName,'HowtorenderGeoIpinformation[default:short]')[CompletionResult]::new('-M','-M',[CompletionResultType]::ParameterName,'Themaximumnumberofaddressestoshowperhop[default:auto]')[CompletionResult]::new('--tui-max-addrs','--tui-max-addrs',[CompletionResultType]::ParameterName,'Themaximumnumberofaddressestoshowperhop[default:auto]')[CompletionResult]::new('--tui-refresh-rate','--tui-refresh-rate',[CompletionResultType]::ParameterName,'TheTuirefreshrate[default:100ms]')[CompletionResult]::new('--tui-privacy-max-ttl','--tui-privacy-max-ttl',[CompletionResultType]::ParameterName,'Themaximumttlofhopswhichwillbemaskedforprivacy[default:0]')[CompletionResult]::new('--tui-locale','--tui-locale',[CompletionResultType]::ParameterName,'ThelocaletousefortheTUI[default:auto]')[CompletionResult]::new('--tui-theme-colors','--tui-theme-colors',[CompletionResultType]::ParameterName,'TheTUIthemecolors[item=color,item=color,..]')[CompletionResult]::new('--tui-key-bindings','--tui-key-bindings',[CompletionResultType]::ParameterName,'TheTUIkeybindings[command=key,command=key,..]')[CompletionResult]::new('-C','-C',[CompletionResultType]::ParameterName,'Thenumberofreportcyclestorun[default:10]')[CompletionResult]::new('--report-cycles','--report-cycles',[CompletionResultType]::ParameterName,'Thenumberofreportcyclestorun[default:10]')[CompletionResult]::new('-G','-G',[CompletionResultType]::ParameterName,'ThesupportedMaxMindorIPinfoGeoIpmmdbfile')[CompletionResult]::new('--geoip-mmdb-file','--geoip-mmdb-file',[CompletionResultType]::ParameterName,'ThesupportedMaxMindorIPinfoGeoIpmmdbfile')[CompletionResult]::new('--generate','--generate',[CompletionResultType]::ParameterName,'Generateshellcompletion')[CompletionResult]::new('--log-format','--log-format',[CompletionResultType]::ParameterName,'Thedebuglogformat[default:pretty]')[CompletionResult]::new('--log-filter','--log-filter',[CompletionResultType]::ParameterName,'Thedebuglogfilter[default:trippy=debug]')[CompletionResult]::new('--log-span-events','--log-span-events',[CompletionResultType]::ParameterName,'Thedebuglogformat[default:off]')[CompletionResult]::new('-u','-u',[CompletionResultType]::ParameterName,'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]')[CompletionResult]::new('--unprivileged','--unprivileged',[CompletionResultType]::ParameterName,'Tracewithoutrequiringelevatedprivilegesonsupportedplatforms[default:false]')[CompletionResult]::new('--udp','--udp',[CompletionResultType]::ParameterName,'TraceusingtheUDPprotocol')[CompletionResult]::new('--tcp','--tcp',[CompletionResultType]::ParameterName,'TraceusingtheTCPprotocol')[CompletionResult]::new('--icmp','--icmp',[CompletionResultType]::ParameterName,'TraceusingtheICMPprotocol')[CompletionResult]::new('-4','-4',[CompletionResultType]::ParameterName,'UseIPv4only')[CompletionResult]::new('--ipv4','--ipv4',[CompletionResultType]::ParameterName,'UseIPv4only')[CompletionResult]::new('-6','-6',[CompletionResultType]::ParameterName,'UseIPv6only')[CompletionResult]::new('--ipv6','--ipv6',[CompletionResultType]::ParameterName,'UseIPv6only')[CompletionResult]::new('-e','-e',[CompletionResultType]::ParameterName,'ParseICMPextensions')[CompletionResult]::new('--icmp-extensions','--icmp-extensions',[CompletionResultType]::ParameterName,'ParseICMPextensions')[CompletionResult]::new('-y','-y',[CompletionResultType]::ParameterName,'TracetoallIPsresolvedfromDNSlookup[default:false]')[CompletionResult]::new('--dns-resolve-all','--dns-resolve-all',[CompletionResultType]::ParameterName,'TracetoallIPsresolvedfromDNSlookup[default:false]')[CompletionResult]::new('-z','-z',[CompletionResultType]::ParameterName,'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]')[CompletionResult]::new('--dns-lookup-as-info','--dns-lookup-as-info',[CompletionResultType]::ParameterName,'Lookupautonomoussystem(AS)informationduringDNSqueries[default:false]')[CompletionResult]::new('--tui-preserve-screen','--tui-preserve-screen',[CompletionResultType]::ParameterName,'Preservethescreenonexit[default:false]')[CompletionResult]::new('--print-tui-theme-items','--print-tui-theme-items',[CompletionResultType]::ParameterName,'PrintallTUIthemeitemsandexit')[CompletionResult]::new('--print-tui-binding-commands','--print-tui-binding-commands',[CompletionResultType]::ParameterName,'PrintallTUIcommandsthatcanbeboundandexit')[CompletionResult]::new('--generate-man','--generate-man',[CompletionResultType]::ParameterName,'GenerateROFFmanpage')[CompletionResult]::new('--print-config-template','--print-config-template',[CompletionResultType]::ParameterName,'Printatemplatetomlconfigfileandexit')[CompletionResult]::new('-v','-v',[CompletionResultType]::ParameterName,'Enableverbosedebuglogging')[CompletionResult]::new('--verbose','--verbose',[CompletionResultType]::ParameterName,'Enableverbosedebuglogging')[CompletionResult]::new('-h','-h',[CompletionResultType]::ParameterName,'Printhelp(seemorewith''--help'')')[CompletionResult]::new('--help','--help',[CompletionResultType]::ParameterName,'Printhelp(seemorewith''--help'')')[CompletionResult]::new('-V','-V',[CompletionResultType]::ParameterName,'Printversion')[CompletionResult]::new('--version','--version',[CompletionResultType]::ParameterName,'Printversion')break}})$completions.Where{$_.CompletionText-like"$wordToComplete*"}|Sort-Object-PropertyListItemText}
diff --git a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_zsh_shell_completions.snap b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_zsh_shell_completions.snap
index 78ecb93ec..cce43bc68 100644
--- a/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_zsh_shell_completions.snap
+++ b/crates/trippy-tui/tests/resources/snapshots/trippy_tui__print__tests__output@generate_zsh_shell_completions.snap
@@ -1,4 +1,4 @@
 ---
 source: crates/trippy-tui/src/print.rs
 ---
-#compdeftripautoload-Uis-at-least_trip(){typeset-Aopt_argstypeset-a_arguments_optionslocalret=1ifis-at-least5.2;then_arguments_options=(-s-S-C)else_arguments_options=(-s-C)filocalcontextcurcontext="$curcontext"stateline_arguments"${_arguments_options[@]}":\'-c+[Configfile]:CONFIG_FILE:_files'\'--config-file=[Configfile]:CONFIG_FILE:_files'\'-m+[Outputmode\[default\:tui\]]:MODE:((tui\:"DisplayinteractiveTUI"stream\:"Displayacontinuousstreamoftracingdata"pretty\:"GenerateaprettytexttablereportforNcycles"markdown\:"GenerateaMarkdowntexttablereportforNcycles"csv\:"GenerateaCSVreportforNcycles"json\:"GenerateaJSONreportforNcycles"dot\:"GenerateaGraphvizDOTfileforNcycles"flows\:"DisplayallflowsforNcycles"silent\:"DonotgenerateanytracingoutputforNcycles"))'\'--mode=[Outputmode\[default\:tui\]]:MODE:((tui\:"DisplayinteractiveTUI"stream\:"Displayacontinuousstreamoftracingdata"pretty\:"GenerateaprettytexttablereportforNcycles"markdown\:"GenerateaMarkdowntexttablereportforNcycles"csv\:"GenerateaCSVreportforNcycles"json\:"GenerateaJSONreportforNcycles"dot\:"GenerateaGraphvizDOTfileforNcycles"flows\:"DisplayallflowsforNcycles"silent\:"DonotgenerateanytracingoutputforNcycles"))'\'-p+[Tracingprotocol\[default\:icmp\]]:PROTOCOL:((icmp\:"InternetControlMessageProtocol"udp\:"UserDatagramProtocol"tcp\:"TransmissionControlProtocol"))'\'--protocol=[Tracingprotocol\[default\:icmp\]]:PROTOCOL:((icmp\:"InternetControlMessageProtocol"udp\:"UserDatagramProtocol"tcp\:"TransmissionControlProtocol"))'\'-F+[Theaddressfamily\[default\:Ipv4thenIpv6\]]:ADDR_FAMILY:((ipv4\:"Ipv4only"ipv6\:"Ipv6only"ipv6-then-ipv4\:"Ipv6withafallbacktoIpv4"ipv4-then-ipv6\:"Ipv4withafallbacktoIpv6"))'\'--addr-family=[Theaddressfamily\[default\:Ipv4thenIpv6\]]:ADDR_FAMILY:((ipv4\:"Ipv4only"ipv6\:"Ipv6only"ipv6-then-ipv4\:"Ipv6withafallbacktoIpv4"ipv4-then-ipv6\:"Ipv4withafallbacktoIpv6"))'\'-P+[Thetargetport(TCP&UDPonly)\[default\:80\]]:TARGET_PORT:'\'--target-port=[Thetargetport(TCP&UDPonly)\[default\:80\]]:TARGET_PORT:'\'-S+[Thesourceport(TCP&UDPonly)\[default\:auto\]]:SOURCE_PORT:'\'--source-port=[Thesourceport(TCP&UDPonly)\[default\:auto\]]:SOURCE_PORT:'\'(-I--interface)-A+[ThesourceIPaddress\[default\:auto\]]:SOURCE_ADDRESS:'\'(-I--interface)--source-address=[ThesourceIPaddress\[default\:auto\]]:SOURCE_ADDRESS:'\'-I+[Thenetworkinterface\[default\:auto\]]:INTERFACE:'\'--interface=[Thenetworkinterface\[default\:auto\]]:INTERFACE:'\'-i+[Theminimumdurationofeveryround\[default\:1s\]]:MIN_ROUND_DURATION:'\'--min-round-duration=[Theminimumdurationofeveryround\[default\:1s\]]:MIN_ROUND_DURATION:'\'-T+[Themaximumdurationofeveryround\[default\:1s\]]:MAX_ROUND_DURATION:'\'--max-round-duration=[Themaximumdurationofeveryround\[default\:1s\]]:MAX_ROUND_DURATION:'\'-g+[TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded\[default\:100ms\]]:GRACE_DURATION:'\'--grace-duration=[TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded\[default\:100ms\]]:GRACE_DURATION:'\'--initial-sequence=[Theinitialsequencenumber\[default\:33434\]]:INITIAL_SEQUENCE:'\'-R+[TheEqual-costMulti-Pathroutingstrategy(UDPonly)\[default\:classic\]]:MULTIPATH_STRATEGY:((classic\:"Thesrcordestportisusedtostorethesequencenumber"paris\:"TheUDP\`checksum\`fieldisusedtostorethesequencenumber"dublin\:"TheIP\`identifier\`fieldisusedtostorethesequencenumber"))'\'--multipath-strategy=[TheEqual-costMulti-Pathroutingstrategy(UDPonly)\[default\:classic\]]:MULTIPATH_STRATEGY:((classic\:"Thesrcordestportisusedtostorethesequencenumber"paris\:"TheUDP\`checksum\`fieldisusedtostorethesequencenumber"dublin\:"TheIP\`identifier\`fieldisusedtostorethesequencenumber"))'\'-U+[Themaximumnumberofin-flightICMPechorequests\[default\:24\]]:MAX_INFLIGHT:'\'--max-inflight=[Themaximumnumberofin-flightICMPechorequests\[default\:24\]]:MAX_INFLIGHT:'\'-f+[TheTTLtostartfrom\[default\:1\]]:FIRST_TTL:'\'--first-ttl=[TheTTLtostartfrom\[default\:1\]]:FIRST_TTL:'\'-t+[ThemaximumnumberofTTLhops\[default\:64\]]:MAX_TTL:'\'--max-ttl=[ThemaximumnumberofTTLhops\[default\:64\]]:MAX_TTL:'\'--packet-size=[ThesizeofIPpackettosend(IPheader+ICMPheader+payload)\[default\:84\]]:PACKET_SIZE:'\'--payload-pattern=[TherepeatingpatterninthepayloadoftheICMPpacket\[default\:0\]]:PAYLOAD_PATTERN:'\'-Q+[TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)\[default\:0\]]:TOS:'\'--tos=[TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)\[default\:0\]]:TOS:'\'--read-timeout=[Thesocketreadtimeout\[default\:10ms\]]:READ_TIMEOUT:'\'-r+[HowtoperformDNSqueries\[default\:system\]]:DNS_RESOLVE_METHOD:((system\:"ResolveusingtheOSresolver"resolv\:"Resolveusingthe\`/etc/resolv.conf\`DNSconfiguration"google\:"ResolveusingtheGoogle\`8.8.8.8\`DNSservice"cloudflare\:"ResolveusingtheCloudflare\`1.1.1.1\`DNSservice"))'\'--dns-resolve-method=[HowtoperformDNSqueries\[default\:system\]]:DNS_RESOLVE_METHOD:((system\:"ResolveusingtheOSresolver"resolv\:"Resolveusingthe\`/etc/resolv.conf\`DNSconfiguration"google\:"ResolveusingtheGoogle\`8.8.8.8\`DNSservice"cloudflare\:"ResolveusingtheCloudflare\`1.1.1.1\`DNSservice"))'\'--dns-timeout=[ThemaximumtimetowaittoperformDNSqueries\[default\:5s\]]:DNS_TIMEOUT:'\'--dns-ttl=[Thetime-to-live(TTL)ofDNSentries\[default\:300s\]]:DNS_TTL:'\'-s+[Themaximumnumberofsamplestorecordperhop\[default\:256\]]:MAX_SAMPLES:'\'--max-samples=[Themaximumnumberofsamplestorecordperhop\[default\:256\]]:MAX_SAMPLES:'\'--max-flows=[Themaximumnumberofflowstorecord\[default\:64\]]:MAX_FLOWS:'\'-a+[Howtorenderaddresses\[default\:host\]]:TUI_ADDRESS_MODE:((ip\:"ShowIPaddressonly"host\:"Showreverse-lookupDNShostnameonly"both\:"ShowbothIPaddressandreverse-lookupDNShostname"))'\'--tui-address-mode=[Howtorenderaddresses\[default\:host\]]:TUI_ADDRESS_MODE:((ip\:"ShowIPaddressonly"host\:"Showreverse-lookupDNShostnameonly"both\:"ShowbothIPaddressandreverse-lookupDNShostname"))'\'--tui-as-mode=[HowtorenderASinformation\[default\:asn\]]:TUI_AS_MODE:((asn\:"ShowtheASN"prefix\:"DisplaytheASprefix"country-code\:"Displaythecountrycode"registry\:"Displaytheregistryname"allocated\:"Displaytheallocateddate"name\:"DisplaytheASname"))'\'--tui-custom-columns=[CustomcolumnstobedisplayedintheTUIhopstable\[default\:holsravbwdt\]]:TUI_CUSTOM_COLUMNS:'\'--tui-icmp-extension-mode=[HowtorenderICMPextensions\[default\:off\]]:TUI_ICMP_EXTENSION_MODE:((off\:"Donotshow\`icmp\`extensions"mpls\:"ShowMPLSlabel(s)only"full\:"Showfull\`icmp\`extensiondataforallknownextensions"all\:"Showfull\`icmp\`extensiondataforallclasses"))'\'--tui-geoip-mode=[HowtorenderGeoIpinformation\[default\:short\]]:TUI_GEOIP_MODE:((off\:"DonotdisplayGeoIpdata"short\:"Showshortformat"long\:"Showlongformat"location\:"ShowlatitudeandLongitudeformat"))'\'-M+[Themaximumnumberofaddressestoshowperhop\[default\:auto\]]:TUI_MAX_ADDRS:'\'--tui-max-addrs=[Themaximumnumberofaddressestoshowperhop\[default\:auto\]]:TUI_MAX_ADDRS:'\'--tui-refresh-rate=[TheTuirefreshrate\[default\:100ms\]]:TUI_REFRESH_RATE:'\'--tui-privacy-max-ttl=[Themaximumttlofhopswhichwillbemaskedforprivacy\[default\:0\]]:TUI_PRIVACY_MAX_TTL:'\'*--tui-theme-colors=[TheTUIthemecolors\[item=color,item=color,..\]]:TUI_THEME_COLORS:'\'*--tui-key-bindings=[TheTUIkeybindings\[command=key,command=key,..\]]:TUI_KEY_BINDINGS:'\'-C+[Thenumberofreportcyclestorun\[default\:10\]]:REPORT_CYCLES:'\'--report-cycles=[Thenumberofreportcyclestorun\[default\:10\]]:REPORT_CYCLES:'\'-G+[ThesupportedMaxMindorIPinfoGeoIpmmdbfile]:GEOIP_MMDB_FILE:_files'\'--geoip-mmdb-file=[ThesupportedMaxMindorIPinfoGeoIpmmdbfile]:GEOIP_MMDB_FILE:_files'\'--generate=[Generateshellcompletion]:GENERATE:(bashelvishfishpowershellzsh)'\'--log-format=[Thedebuglogformat\[default\:pretty\]]:LOG_FORMAT:((compact\:"Displaylogdatainacompactformat"pretty\:"Displaylogdatainaprettyformat"json\:"Displaylogdatainajsonformat"chrome\:"DisplaylogdatainChrometraceformat"))'\'--log-filter=[Thedebuglogfilter\[default\:trippy=debug\]]:LOG_FILTER:'\'--log-span-events=[Thedebuglogformat\[default\:off\]]:LOG_SPAN_EVENTS:((off\:"Donotdisplayeventspans"active\:"Displayenterandexiteventspans"full\:"Displayalleventspans"))'\'-u[Tracewithoutrequiringelevatedprivilegesonsupportedplatforms\[default\:false\]]'\'--unprivileged[Tracewithoutrequiringelevatedprivilegesonsupportedplatforms\[default\:false\]]'\'(-p--protocol--tcp--icmp)--udp[TraceusingtheUDPprotocol]'\'(-p--protocol--udp--icmp)--tcp[TraceusingtheTCPprotocol]'\'(-p--protocol--udp--tcp)--icmp[TraceusingtheICMPprotocol]'\'(-6--ipv6-F--addr-family)-4[UseIPv4only]'\'(-6--ipv6-F--addr-family)--ipv4[UseIPv4only]'\'(-4--ipv4-F--addr-family)-6[UseIPv6only]'\'(-4--ipv4-F--addr-family)--ipv6[UseIPv6only]'\'-e[ParseICMPextensions]'\'--icmp-extensions[ParseICMPextensions]'\'-y[TracetoallIPsresolvedfromDNSlookup\[default\:false\]]'\'--dns-resolve-all[TracetoallIPsresolvedfromDNSlookup\[default\:false\]]'\'-z[Lookupautonomoussystem(AS)informationduringDNSqueries\[default\:false\]]'\'--dns-lookup-as-info[Lookupautonomoussystem(AS)informationduringDNSqueries\[default\:false\]]'\'--tui-preserve-screen[Preservethescreenonexit\[default\:false\]]'\'--print-tui-theme-items[PrintallTUIthemeitemsandexit]'\'--print-tui-binding-commands[PrintallTUIcommandsthatcanbeboundandexit]'\'--generate-man[GenerateROFFmanpage]'\'--print-config-template[Printatemplatetomlconfigfileandexit]'\'-v[Enableverbosedebuglogging]'\'--verbose[Enableverbosedebuglogging]'\'-h[Printhelp(seemorewith'\''--help'\'')]'\'--help[Printhelp(seemorewith'\''--help'\'')]'\'-V[Printversion]'\'--version[Printversion]'\'*::targets--AspacedelimitedlistofhostnamesandIPstotrace:'\&&ret=0}(($+functions[_trip_commands]))||_trip_commands(){localcommands;commands=()_describe-tcommands'tripcommands'commands"$@"}if["$funcstack[1]"="_trip"];then_trip"$@"elsecompdef_triptripfi
+#compdeftripautoload-Uis-at-least_trip(){typeset-Aopt_argstypeset-a_arguments_optionslocalret=1ifis-at-least5.2;then_arguments_options=(-s-S-C)else_arguments_options=(-s-C)filocalcontextcurcontext="$curcontext"stateline_arguments"${_arguments_options[@]}":\'-c+[Configfile]:CONFIG_FILE:_files'\'--config-file=[Configfile]:CONFIG_FILE:_files'\'-m+[Outputmode\[default\:tui\]]:MODE:((tui\:"DisplayinteractiveTUI"stream\:"Displayacontinuousstreamoftracingdata"pretty\:"GenerateaprettytexttablereportforNcycles"markdown\:"GenerateaMarkdowntexttablereportforNcycles"csv\:"GenerateaCSVreportforNcycles"json\:"GenerateaJSONreportforNcycles"dot\:"GenerateaGraphvizDOTfileforNcycles"flows\:"DisplayallflowsforNcycles"silent\:"DonotgenerateanytracingoutputforNcycles"))'\'--mode=[Outputmode\[default\:tui\]]:MODE:((tui\:"DisplayinteractiveTUI"stream\:"Displayacontinuousstreamoftracingdata"pretty\:"GenerateaprettytexttablereportforNcycles"markdown\:"GenerateaMarkdowntexttablereportforNcycles"csv\:"GenerateaCSVreportforNcycles"json\:"GenerateaJSONreportforNcycles"dot\:"GenerateaGraphvizDOTfileforNcycles"flows\:"DisplayallflowsforNcycles"silent\:"DonotgenerateanytracingoutputforNcycles"))'\'-p+[Tracingprotocol\[default\:icmp\]]:PROTOCOL:((icmp\:"InternetControlMessageProtocol"udp\:"UserDatagramProtocol"tcp\:"TransmissionControlProtocol"))'\'--protocol=[Tracingprotocol\[default\:icmp\]]:PROTOCOL:((icmp\:"InternetControlMessageProtocol"udp\:"UserDatagramProtocol"tcp\:"TransmissionControlProtocol"))'\'-F+[Theaddressfamily\[default\:Ipv4thenIpv6\]]:ADDR_FAMILY:((ipv4\:"Ipv4only"ipv6\:"Ipv6only"ipv6-then-ipv4\:"Ipv6withafallbacktoIpv4"ipv4-then-ipv6\:"Ipv4withafallbacktoIpv6"))'\'--addr-family=[Theaddressfamily\[default\:Ipv4thenIpv6\]]:ADDR_FAMILY:((ipv4\:"Ipv4only"ipv6\:"Ipv6only"ipv6-then-ipv4\:"Ipv6withafallbacktoIpv4"ipv4-then-ipv6\:"Ipv4withafallbacktoIpv6"))'\'-P+[Thetargetport(TCP&UDPonly)\[default\:80\]]:TARGET_PORT:'\'--target-port=[Thetargetport(TCP&UDPonly)\[default\:80\]]:TARGET_PORT:'\'-S+[Thesourceport(TCP&UDPonly)\[default\:auto\]]:SOURCE_PORT:'\'--source-port=[Thesourceport(TCP&UDPonly)\[default\:auto\]]:SOURCE_PORT:'\'(-I--interface)-A+[ThesourceIPaddress\[default\:auto\]]:SOURCE_ADDRESS:'\'(-I--interface)--source-address=[ThesourceIPaddress\[default\:auto\]]:SOURCE_ADDRESS:'\'-I+[Thenetworkinterface\[default\:auto\]]:INTERFACE:'\'--interface=[Thenetworkinterface\[default\:auto\]]:INTERFACE:'\'-i+[Theminimumdurationofeveryround\[default\:1s\]]:MIN_ROUND_DURATION:'\'--min-round-duration=[Theminimumdurationofeveryround\[default\:1s\]]:MIN_ROUND_DURATION:'\'-T+[Themaximumdurationofeveryround\[default\:1s\]]:MAX_ROUND_DURATION:'\'--max-round-duration=[Themaximumdurationofeveryround\[default\:1s\]]:MAX_ROUND_DURATION:'\'-g+[TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded\[default\:100ms\]]:GRACE_DURATION:'\'--grace-duration=[TheperiodoftimetowaitforadditionalICMPresponsesafterthetargethasresponded\[default\:100ms\]]:GRACE_DURATION:'\'--initial-sequence=[Theinitialsequencenumber\[default\:33434\]]:INITIAL_SEQUENCE:'\'-R+[TheEqual-costMulti-Pathroutingstrategy(UDPonly)\[default\:classic\]]:MULTIPATH_STRATEGY:((classic\:"Thesrcordestportisusedtostorethesequencenumber"paris\:"TheUDP\`checksum\`fieldisusedtostorethesequencenumber"dublin\:"TheIP\`identifier\`fieldisusedtostorethesequencenumber"))'\'--multipath-strategy=[TheEqual-costMulti-Pathroutingstrategy(UDPonly)\[default\:classic\]]:MULTIPATH_STRATEGY:((classic\:"Thesrcordestportisusedtostorethesequencenumber"paris\:"TheUDP\`checksum\`fieldisusedtostorethesequencenumber"dublin\:"TheIP\`identifier\`fieldisusedtostorethesequencenumber"))'\'-U+[Themaximumnumberofin-flightICMPechorequests\[default\:24\]]:MAX_INFLIGHT:'\'--max-inflight=[Themaximumnumberofin-flightICMPechorequests\[default\:24\]]:MAX_INFLIGHT:'\'-f+[TheTTLtostartfrom\[default\:1\]]:FIRST_TTL:'\'--first-ttl=[TheTTLtostartfrom\[default\:1\]]:FIRST_TTL:'\'-t+[ThemaximumnumberofTTLhops\[default\:64\]]:MAX_TTL:'\'--max-ttl=[ThemaximumnumberofTTLhops\[default\:64\]]:MAX_TTL:'\'--packet-size=[ThesizeofIPpackettosend(IPheader+ICMPheader+payload)\[default\:84\]]:PACKET_SIZE:'\'--payload-pattern=[TherepeatingpatterninthepayloadoftheICMPpacket\[default\:0\]]:PAYLOAD_PATTERN:'\'-Q+[TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)\[default\:0\]]:TOS:'\'--tos=[TheTOS(i.e.DSCP+ECN)IPheadervalue(TCPandUDPonly)\[default\:0\]]:TOS:'\'--read-timeout=[Thesocketreadtimeout\[default\:10ms\]]:READ_TIMEOUT:'\'-r+[HowtoperformDNSqueries\[default\:system\]]:DNS_RESOLVE_METHOD:((system\:"ResolveusingtheOSresolver"resolv\:"Resolveusingthe\`/etc/resolv.conf\`DNSconfiguration"google\:"ResolveusingtheGoogle\`8.8.8.8\`DNSservice"cloudflare\:"ResolveusingtheCloudflare\`1.1.1.1\`DNSservice"))'\'--dns-resolve-method=[HowtoperformDNSqueries\[default\:system\]]:DNS_RESOLVE_METHOD:((system\:"ResolveusingtheOSresolver"resolv\:"Resolveusingthe\`/etc/resolv.conf\`DNSconfiguration"google\:"ResolveusingtheGoogle\`8.8.8.8\`DNSservice"cloudflare\:"ResolveusingtheCloudflare\`1.1.1.1\`DNSservice"))'\'--dns-timeout=[ThemaximumtimetowaittoperformDNSqueries\[default\:5s\]]:DNS_TIMEOUT:'\'--dns-ttl=[Thetime-to-live(TTL)ofDNSentries\[default\:300s\]]:DNS_TTL:'\'-s+[Themaximumnumberofsamplestorecordperhop\[default\:256\]]:MAX_SAMPLES:'\'--max-samples=[Themaximumnumberofsamplestorecordperhop\[default\:256\]]:MAX_SAMPLES:'\'--max-flows=[Themaximumnumberofflowstorecord\[default\:64\]]:MAX_FLOWS:'\'-a+[Howtorenderaddresses\[default\:host\]]:TUI_ADDRESS_MODE:((ip\:"ShowIPaddressonly"host\:"Showreverse-lookupDNShostnameonly"both\:"ShowbothIPaddressandreverse-lookupDNShostname"))'\'--tui-address-mode=[Howtorenderaddresses\[default\:host\]]:TUI_ADDRESS_MODE:((ip\:"ShowIPaddressonly"host\:"Showreverse-lookupDNShostnameonly"both\:"ShowbothIPaddressandreverse-lookupDNShostname"))'\'--tui-as-mode=[HowtorenderASinformation\[default\:asn\]]:TUI_AS_MODE:((asn\:"ShowtheASN"prefix\:"DisplaytheASprefix"country-code\:"Displaythecountrycode"registry\:"Displaytheregistryname"allocated\:"Displaytheallocateddate"name\:"DisplaytheASname"))'\'--tui-custom-columns=[CustomcolumnstobedisplayedintheTUIhopstable\[default\:holsravbwdt\]]:TUI_CUSTOM_COLUMNS:'\'--tui-icmp-extension-mode=[HowtorenderICMPextensions\[default\:off\]]:TUI_ICMP_EXTENSION_MODE:((off\:"Donotshow\`icmp\`extensions"mpls\:"ShowMPLSlabel(s)only"full\:"Showfull\`icmp\`extensiondataforallknownextensions"all\:"Showfull\`icmp\`extensiondataforallclasses"))'\'--tui-geoip-mode=[HowtorenderGeoIpinformation\[default\:short\]]:TUI_GEOIP_MODE:((off\:"DonotdisplayGeoIpdata"short\:"Showshortformat"long\:"Showlongformat"location\:"ShowlatitudeandLongitudeformat"))'\'-M+[Themaximumnumberofaddressestoshowperhop\[default\:auto\]]:TUI_MAX_ADDRS:'\'--tui-max-addrs=[Themaximumnumberofaddressestoshowperhop\[default\:auto\]]:TUI_MAX_ADDRS:'\'--tui-refresh-rate=[TheTuirefreshrate\[default\:100ms\]]:TUI_REFRESH_RATE:'\'--tui-privacy-max-ttl=[Themaximumttlofhopswhichwillbemaskedforprivacy\[default\:0\]]:TUI_PRIVACY_MAX_TTL:'\'--tui-locale=[ThelocaletousefortheTUI\[default\:auto\]]:TUI_LOCALE:'\'*--tui-theme-colors=[TheTUIthemecolors\[item=color,item=color,..\]]:TUI_THEME_COLORS:'\'*--tui-key-bindings=[TheTUIkeybindings\[command=key,command=key,..\]]:TUI_KEY_BINDINGS:'\'-C+[Thenumberofreportcyclestorun\[default\:10\]]:REPORT_CYCLES:'\'--report-cycles=[Thenumberofreportcyclestorun\[default\:10\]]:REPORT_CYCLES:'\'-G+[ThesupportedMaxMindorIPinfoGeoIpmmdbfile]:GEOIP_MMDB_FILE:_files'\'--geoip-mmdb-file=[ThesupportedMaxMindorIPinfoGeoIpmmdbfile]:GEOIP_MMDB_FILE:_files'\'--generate=[Generateshellcompletion]:GENERATE:(bashelvishfishpowershellzsh)'\'--log-format=[Thedebuglogformat\[default\:pretty\]]:LOG_FORMAT:((compact\:"Displaylogdatainacompactformat"pretty\:"Displaylogdatainaprettyformat"json\:"Displaylogdatainajsonformat"chrome\:"DisplaylogdatainChrometraceformat"))'\'--log-filter=[Thedebuglogfilter\[default\:trippy=debug\]]:LOG_FILTER:'\'--log-span-events=[Thedebuglogformat\[default\:off\]]:LOG_SPAN_EVENTS:((off\:"Donotdisplayeventspans"active\:"Displayenterandexiteventspans"full\:"Displayalleventspans"))'\'-u[Tracewithoutrequiringelevatedprivilegesonsupportedplatforms\[default\:false\]]'\'--unprivileged[Tracewithoutrequiringelevatedprivilegesonsupportedplatforms\[default\:false\]]'\'(-p--protocol--tcp--icmp)--udp[TraceusingtheUDPprotocol]'\'(-p--protocol--udp--icmp)--tcp[TraceusingtheTCPprotocol]'\'(-p--protocol--udp--tcp)--icmp[TraceusingtheICMPprotocol]'\'(-6--ipv6-F--addr-family)-4[UseIPv4only]'\'(-6--ipv6-F--addr-family)--ipv4[UseIPv4only]'\'(-4--ipv4-F--addr-family)-6[UseIPv6only]'\'(-4--ipv4-F--addr-family)--ipv6[UseIPv6only]'\'-e[ParseICMPextensions]'\'--icmp-extensions[ParseICMPextensions]'\'-y[TracetoallIPsresolvedfromDNSlookup\[default\:false\]]'\'--dns-resolve-all[TracetoallIPsresolvedfromDNSlookup\[default\:false\]]'\'-z[Lookupautonomoussystem(AS)informationduringDNSqueries\[default\:false\]]'\'--dns-lookup-as-info[Lookupautonomoussystem(AS)informationduringDNSqueries\[default\:false\]]'\'--tui-preserve-screen[Preservethescreenonexit\[default\:false\]]'\'--print-tui-theme-items[PrintallTUIthemeitemsandexit]'\'--print-tui-binding-commands[PrintallTUIcommandsthatcanbeboundandexit]'\'--generate-man[GenerateROFFmanpage]'\'--print-config-template[Printatemplatetomlconfigfileandexit]'\'-v[Enableverbosedebuglogging]'\'--verbose[Enableverbosedebuglogging]'\'-h[Printhelp(seemorewith'\''--help'\'')]'\'--help[Printhelp(seemorewith'\''--help'\'')]'\'-V[Printversion]'\'--version[Printversion]'\'*::targets--AspacedelimitedlistofhostnamesandIPstotrace:'\&&ret=0}(($+functions[_trip_commands]))||_trip_commands(){localcommands;commands=()_describe-tcommands'tripcommands'commands"$@"}if["$funcstack[1]"="_trip"];then_trip"$@"elsecompdef_triptripfi