Skip to content

Commit b4cdb63

Browse files
committed
feat(i18n): support for project fluent
1 parent 405a94d commit b4cdb63

File tree

14 files changed

+946
-10
lines changed

14 files changed

+946
-10
lines changed

Diff for: Cargo.lock

+757-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: crates/atuin-client/Cargo.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ edition = "2024"
44
description = "client library for atuin"
55

66
rust-version = { workspace = true }
7-
version = { workspace = true }
7+
version = "18.5.0-beta.2"
88
authors = { workspace = true }
99
license = { workspace = true }
1010
homepage = { workspace = true }
@@ -20,6 +20,7 @@ check-update = []
2020

2121
[dependencies]
2222
atuin-common = { path = "../atuin-common", version = "18.5.0-beta.2" }
23+
atuin-macro = { path = "../atuin-macro", version = "18.5.0-beta.2" }
2324

2425
log = { workspace = true }
2526
base64 = { workspace = true }
@@ -73,6 +74,11 @@ palette = { version = "0.7.5", features = ["serializing"] }
7374
lazy_static = "1.4.0"
7475
strum_macros = "0.26.3"
7576
strum = { version = "0.26.2", features = ["strum_macros"] }
77+
fluent = "0.16.1"
78+
i18n-embed = { version = "0.15.3", features = ["autoreload", "desktop-requester", "filesystem-assets", "fluent", "fluent-syntax", "fluent-system", "gettext-system", "locale_config", "notify", "tr", "walkdir"] }
79+
rust-embed = "8"
80+
i18n-embed-fl = "0.9.3"
81+
cargo-i18n = "0.2.13"
7682

7783
[dev-dependencies]
7884
tokio = { version = "1", features = ["full"] }

Diff for: crates/atuin-common/Cargo.toml

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ edition = "2024"
44
description = "common library for atuin"
55

66
rust-version = { workspace = true }
7-
version = { workspace = true }
7+
version = "18.5.0-beta.2" # required to be concrete for cargo i18n
88
authors = { workspace = true }
99
license = { workspace = true }
1010
homepage = { workspace = true }
@@ -13,6 +13,8 @@ repository = { workspace = true }
1313
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1414

1515
[dependencies]
16+
atuin-macro = { path = "../atuin-macro" }
17+
1618
time = { workspace = true }
1719
serde = { workspace = true }
1820
uuid = { workspace = true }
@@ -25,8 +27,14 @@ directories = { workspace = true }
2527
sysinfo = "0.30.7"
2628
base64 = { workspace = true }
2729
getrandom = "0.2"
30+
sys-locale = "0.3.2"
2831

2932
lazy_static = "1.4.0"
33+
i18n-embed = { version = "0.15.3", features = ["fluent", "fluent-system", "tr", "locale_config", "desktop-requester", "walkdir", "gettext-system", "autoreload", "filesystem-assets", "notify"] }
34+
rust-embed = "8"
35+
i18n-embed-fl = "0.9.3"
36+
slugify = "0.1.0"
37+
paste = "1.0.15"
3038

3139
[dev-dependencies]
3240
pretty_assertions = { workspace = true }

Diff for: crates/atuin-common/i18n.toml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# (Required) The language identifier of the language used in the
2+
# source code for gettext system, and the primary fallback language
3+
# (for which all strings must be present) when using the fluent
4+
# system.
5+
fallback_language = "en-US"
6+
7+
# Use the fluent localization system.
8+
[fluent]
9+
domain = "atuin"
10+
# (Required) The path to the assets directory.
11+
# The paths inside the assets directory should be structured like so:
12+
# `assets_dir/{language}/{domain}.ftl`
13+
assets_dir = "../../i18n"

Diff for: crates/atuin-common/src/i18n.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use i18n_embed::{DesktopLanguageRequester, fluent::{
2+
FluentLanguageLoader, fluent_language_loader
3+
}};
4+
use rust_embed::RustEmbed;
5+
pub use i18n_embed_fl::fl;
6+
7+
#[derive(RustEmbed)]
8+
#[folder = "../../i18n"] // path to the compiled localization resources
9+
struct Localizations;
10+
11+
use lazy_static::lazy_static;
12+
pub use atuin_macro::tl;
13+
14+
lazy_static! {
15+
pub static ref LOADER: FluentLanguageLoader = {
16+
// Load languages from central internationalization folder.
17+
let language_loader: FluentLanguageLoader = fluent_language_loader!();
18+
let requested_languages = DesktopLanguageRequester::requested_languages();
19+
20+
let _result = i18n_embed::select(
21+
&language_loader, &Localizations, &requested_languages);
22+
language_loader
23+
};
24+
}
25+
26+
#[macro_export]
27+
macro_rules! t {
28+
($message_id:literal) => {
29+
$crate::i18n::tl!($crate::i18n::fl, $crate::i18n::LOADER, $message_id)
30+
};
31+
32+
($message_id:literal, $($args:expr),*) => {{
33+
$crate::i18n::tl!($crate::i18n::fl, $crate::i18n::LOADER, $message_id, $($args), *)
34+
}};
35+
}
36+
37+
pub use t;

Diff for: crates/atuin-common/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@ pub mod api;
5757
pub mod record;
5858
pub mod shell;
5959
pub mod utils;
60+
pub mod i18n;

Diff for: crates/atuin-macro/Cargo.toml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
[package]
2+
name = "atuin-macro"
3+
edition = "2021"
4+
description = "macro library for atuin"
5+
6+
rust-version = { workspace = true }
7+
version = "18.5.0-beta.2"
8+
authors = { workspace = true }
9+
license = { workspace = true }
10+
homepage = { workspace = true }
11+
repository = { workspace = true }
12+
13+
[lib]
14+
proc-macro = true
15+
16+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
17+
18+
[dependencies]
19+
time = { workspace = true }
20+
serde = { workspace = true }
21+
uuid = { workspace = true }
22+
typed-builder = { workspace = true }
23+
eyre = { workspace = true }
24+
sqlx = { workspace = true }
25+
semver = { workspace = true }
26+
thiserror = { workspace = true }
27+
directories = { workspace = true }
28+
sysinfo = "0.30.7"
29+
base64 = { workspace = true }
30+
getrandom = "0.2"
31+
sys-locale = "0.3.2"
32+
33+
lazy_static = "1.4.0"
34+
i18n-embed = { version = "0.15.3", features = ["fluent", "fluent-system", "tr", "locale_config", "desktop-requester", "walkdir", "gettext-system", "autoreload", "filesystem-assets", "notify"] }
35+
rust-embed = "8"
36+
i18n-embed-fl = "0.9.3"
37+
slugify = "0.1.0"
38+
paste = "1.0.15"
39+
syn = "2.0.96"
40+
quote = "1.0.38"
41+
proc-macro2 = "1.0.93"

Diff for: crates/atuin-macro/src/lib.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#![forbid(unsafe_code)]
2+
extern crate proc_macro;
3+
4+
use proc_macro::TokenStream;
5+
use quote::quote;
6+
use syn;
7+
use syn::parse::Parser;
8+
use slugify::slugify;
9+
10+
#[proc_macro]
11+
pub fn tl(tokens: TokenStream) -> TokenStream {
12+
let args = syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated.parse(tokens).unwrap();
13+
let mut arg_iter = args.iter();
14+
let fl = arg_iter.next().unwrap();
15+
let loader = arg_iter.next().unwrap();
16+
let message_id: proc_macro2::TokenStream = match arg_iter.next() {
17+
Some(syn::Expr::Group(arg)) => {
18+
match *arg.expr.clone() {
19+
syn::Expr::Lit(arg) => {
20+
let quoted: String = match arg.lit.clone() {
21+
syn::Lit::Str(message_id) => message_id.value(),
22+
_ => panic!("Message ID must be a literal string")
23+
};
24+
let slug = slugify!(quoted.as_str());
25+
quote!(#slug)
26+
},
27+
arg => panic!("Message ID {:?} must be a literal", arg)
28+
}
29+
},
30+
Some(syn::Expr::Lit(arg)) => {
31+
let quoted: String = match arg.lit.clone() {
32+
syn::Lit::Str(message_id) => message_id.value(),
33+
_ => panic!("Message ID must be a literal string")
34+
};
35+
let slug = slugify!(quoted.as_str());
36+
quote!(#slug)
37+
},
38+
arg => panic!("Message ID {:?} must be a literal", arg)
39+
};
40+
let args: Vec<_> = arg_iter.collect();
41+
42+
if args.is_empty() {
43+
TokenStream::from(quote!(
44+
#fl!(#loader, #message_id)
45+
))
46+
} else {
47+
TokenStream::from(quote!(
48+
#fl!(#loader, #message_id, #(#args),*)
49+
))
50+
}
51+
}
52+

Diff for: crates/atuin/Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description = "atuin - magical shell history"
55
readme = "./README.md"
66

77
rust-version = { workspace = true }
8-
version = { workspace = true }
8+
version = "18.5.0-beta.2"
99
authors = { workspace = true }
1010
license = { workspace = true }
1111
homepage = { workspace = true }
@@ -80,6 +80,9 @@ tracing-subscriber = { workspace = true }
8080
uuid = { workspace = true }
8181
sysinfo = "0.30.7"
8282
regex = "1.10.5"
83+
i18n-embed = { version = "0.15.3", features = ["fluent", "fluent-system", "tr", "locale_config", "desktop-requester", "walkdir", "gettext-system", "autoreload", "filesystem-assets", "notify"] }
84+
rust-embed = "8"
85+
i18n-embed-fl = "0.9.3"
8386

8487
[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies]
8588
arboard = { version = "3.4", optional = true }

Diff for: crates/atuin/i18n.toml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# (Required) The language identifier of the language used in the
2+
# source code for gettext system, and the primary fallback language
3+
# (for which all strings must be present) when using the fluent
4+
# system.
5+
fallback_language = "en-US"
6+
7+
# Use the fluent localization system.
8+
[fluent]
9+
domain = "atuin"
10+
11+
# (Required) The path to the assets directory.
12+
# The paths inside the assets directory should be structured like so:
13+
# `assets_dir/{language}/{domain}.ftl`
14+
assets_dir = "../../i18n"

Diff for: crates/atuin/src/command/external.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ fn render_not_found(subcommand: &str, bin: &str) -> StyledStr {
5454
let _ = write!(output, "{error}error:{error:#} ");
5555
let _ = write!(
5656
output,
57-
"unrecognized subcommand '{invalid}{subcommand}{invalid:#}' "
57+
"{} ",
58+
t!(
59+
"unrecognized subcommand '%{subcommand}'",
60+
subcommand = format!("{invalid}{subcommand}{invalid:#}")
61+
)
5862
);
5963
let _ = write!(
6064
output,

Diff for: crates/atuin/src/main.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#![warn(clippy::pedantic, clippy::nursery)]
22
#![allow(clippy::use_self, clippy::missing_const_for_fn)] // not 100% reliable
3+
#[macro_use]
4+
extern crate atuin_common;
35

46
use clap::Parser;
57
use eyre::Result;

Diff for: i18n/en-GB/atuin.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
unrecognized-subcommand-subcommand =
2+
unrecognised subcommand '{ $subcommand }'

Diff for: i18n/en-US/atuin.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
unrecognized-subcommand-subcommand =
2+
unrecognized subcommand '{ $subcommand }'

0 commit comments

Comments
 (0)