Skip to content

Commit 5510baa

Browse files
Add typesafe message arguments example
1 parent 862df16 commit 5510baa

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use std::borrow::Cow;
2+
3+
use fluent_bundle::{ArgumentResolver, FluentBundle, FluentError, FluentResource, FluentValue};
4+
use unic_langid::langid;
5+
6+
fn main() {
7+
let ftl_string = String::from(
8+
"
9+
hello-world = Hello { $name }
10+
ref = The previous message says { hello-world }
11+
unread-emails =
12+
{ $emailCount ->
13+
[one] You have { $emailCount } unread email
14+
*[other] You have { $emailCount } unread emails
15+
}
16+
",
17+
);
18+
let res = FluentResource::try_new(ftl_string).expect("Could not parse an FTL string.");
19+
let langid_en = langid!("en");
20+
let mut bundle = FluentBundle::new(vec![langid_en]);
21+
bundle
22+
.add_resource(res)
23+
.expect("Failed to add FTL resources to the bundle.");
24+
25+
let hello_world = messages::HelloWorld { name: "John" };
26+
let mut errors = vec![];
27+
let value = bundle.format_message(&hello_world, &mut errors);
28+
println!("{}", value);
29+
30+
let ref_msg = messages::Ref { hello_world };
31+
let mut errors = vec![];
32+
let value = bundle.format_message(&ref_msg, &mut errors);
33+
println!("{}", value);
34+
35+
let unread_emails = messages::UnreadEmails {
36+
email_count: Some(1),
37+
};
38+
let mut errors = vec![];
39+
let value = bundle.format_message(&unread_emails, &mut errors);
40+
println!("{}", value);
41+
}
42+
43+
// these definitions could be generated by a macro or a code generation tool
44+
mod messages {
45+
use super::*;
46+
47+
pub struct HelloWorld<'a> {
48+
pub name: &'a str,
49+
}
50+
51+
impl<'a> Message<'a> for HelloWorld<'a> {
52+
fn id(&self) -> &'static str {
53+
"hello-world"
54+
}
55+
56+
fn get_arg(&self, name: &str) -> Option<FluentValue<'a>> {
57+
Some(match name {
58+
"name" => self.name.into(),
59+
_ => return None,
60+
})
61+
}
62+
}
63+
64+
pub struct Ref<'a> {
65+
pub hello_world: HelloWorld<'a>,
66+
}
67+
68+
impl<'a> Message<'a> for Ref<'a> {
69+
fn id(&self) -> &'static str {
70+
"ref"
71+
}
72+
73+
fn get_arg(&self, name: &str) -> Option<FluentValue<'a>> {
74+
self.hello_world.get_arg(name)
75+
}
76+
}
77+
78+
pub struct UnreadEmails {
79+
pub email_count: Option<u32>,
80+
}
81+
82+
impl<'a> Message<'a> for UnreadEmails {
83+
fn id(&self) -> &'static str {
84+
"unread-emails"
85+
}
86+
87+
fn get_arg(&self, name: &str) -> Option<FluentValue<'a>> {
88+
Some(match name {
89+
"emailCount" => self.email_count.into(),
90+
_ => return None,
91+
})
92+
}
93+
}
94+
}
95+
96+
trait Message<'a> {
97+
fn id(&self) -> &'static str;
98+
fn get_arg(&self, name: &str) -> Option<FluentValue<'a>>;
99+
}
100+
101+
impl<'a, 'b> ArgumentResolver<'a> for &'a dyn Message<'b> {
102+
fn resolve(self, name: &str) -> Option<Cow<FluentValue<'a>>> {
103+
let arg = self.get_arg(name)?;
104+
Some(Cow::Owned(arg))
105+
}
106+
}
107+
108+
trait CustomizedBundle {
109+
fn format_message<'b>(
110+
&'b self,
111+
message: &dyn Message,
112+
errors: &mut Vec<FluentError>,
113+
) -> Cow<'b, str>;
114+
}
115+
116+
impl CustomizedBundle for FluentBundle<FluentResource> {
117+
fn format_message<'b>(
118+
&'b self,
119+
message: &dyn Message,
120+
errors: &mut Vec<FluentError>,
121+
) -> Cow<'b, str> {
122+
let msg = self
123+
.get_message(message.id())
124+
.expect("Message doesn't exist.");
125+
126+
let pattern = msg.value().expect("Message has no value.");
127+
self.format_pattern_with_argument_resolver(pattern, message, errors)
128+
}
129+
}

0 commit comments

Comments
 (0)