Skip to content

Commit 47bcb83

Browse files
authored
Enable direction isolation (#116)
1 parent dcff605 commit 47bcb83

File tree

7 files changed

+82
-11
lines changed

7 files changed

+82
-11
lines changed

fluent-bundle/examples/functions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fn main() {
5757
let (value, _) = bundle
5858
.format("hello-world", None)
5959
.expect("Failed to format a message.");
60-
assert_eq!(&value, "Hey there! I'm a function!");
60+
assert_eq!(&value, "Hey there! \u{2068}I'm a function!\u{2069}");
6161

6262
let (value, _) = bundle
6363
.format("meaning-of-life", None)

fluent-bundle/src/bundle.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub struct Message<'m> {
4747
///
4848
/// let (value, _) = bundle.format("intro", Some(&args))
4949
/// .expect("Failed to format a message.");
50-
/// assert_eq!(&value, "Welcome, Rustacean.");
50+
/// assert_eq!(&value, "Welcome, \u{2068}Rustacean\u{2069}.");
5151
///
5252
/// ```
5353
///
@@ -89,6 +89,7 @@ pub struct FluentBundle<'bundle, R> {
8989
pub(crate) resources: Vec<R>,
9090
pub(crate) entries: HashMap<String, Entry<'bundle>>,
9191
pub(crate) plural_rules: IntlPluralRules,
92+
pub(crate) use_isolating: bool,
9293
}
9394

9495
impl<'bundle, R> FluentBundle<'bundle, R> {
@@ -128,6 +129,7 @@ impl<'bundle, R> FluentBundle<'bundle, R> {
128129
resources: vec![],
129130
entries: HashMap::new(),
130131
plural_rules: pr,
132+
use_isolating: true,
131133
}
132134
}
133135

@@ -217,6 +219,10 @@ impl<'bundle, R> FluentBundle<'bundle, R> {
217219
}
218220
}
219221

222+
pub fn set_use_isolating(&mut self, value: bool) {
223+
self.use_isolating = value;
224+
}
225+
220226
/// Returns true if this bundle contains a message with the given id.
221227
///
222228
/// # Examples
@@ -311,7 +317,7 @@ impl<'bundle, R> FluentBundle<'bundle, R> {
311317
///
312318
/// let (value, _) = bundle.format("intro", Some(&args))
313319
/// .expect("Failed to format a message.");
314-
/// assert_eq!(&value, "Welcome, Rustacean.");
320+
/// assert_eq!(&value, "Welcome, \u{2068}Rustacean\u{2069}.");
315321
///
316322
/// ```
317323
///

fluent-bundle/src/entry.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use crate::bundle::FluentBundle;
99
use crate::resource::FluentResource;
1010
use crate::types::FluentValue;
1111

12-
pub type FluentFunction<'bundle> = Box<dyn
13-
'bundle
12+
pub type FluentFunction<'bundle> = Box<
13+
dyn 'bundle
1414
+ for<'a> Fn(&[FluentValue<'a>], &HashMap<&str, FluentValue<'a>>) -> FluentValue<'a>
1515
+ Send
1616
+ Sync,

fluent-bundle/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
//! .format("intro", Some(&args))
4141
//! .expect("Failed to format a message.");
4242
//!
43-
//! assert_eq!(value, "Welcome, John.");
43+
//! assert_eq!(value, "Welcome, \u{2068}John\u{2069}.");
4444
//! ```
4545
4646
#[macro_use]

fluent-bundle/src/resolve.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::borrow::Borrow;
1010
use std::cell::RefCell;
1111
use std::collections::hash_map::DefaultHasher;
1212
use std::collections::HashMap;
13+
use std::fmt::Write;
1314
use std::hash::{Hash, Hasher};
1415

1516
use fluent_syntax::ast;
@@ -105,9 +106,7 @@ where
105106
if value.elements.len() == 1 {
106107
return match value.elements[0] {
107108
ast::PatternElement::TextElement(s) => FluentValue::String(s.into()),
108-
ast::PatternElement::Placeable(ref p) => {
109-
scope.track(entry, |scope| p.resolve(scope))
110-
}
109+
ast::PatternElement::Placeable(ref p) => scope.track(entry, |scope| p.resolve(scope)),
111110
};
112111
}
113112

@@ -119,7 +118,24 @@ where
119118
}
120119
ast::PatternElement::Placeable(p) => {
121120
let result = scope.track(entry, |scope| p.resolve(scope));
122-
string.push_str(&result.to_string());
121+
let needs_isolation = scope.bundle.use_isolating
122+
&& match p {
123+
ast::Expression::InlineExpression(
124+
ast::InlineExpression::MessageReference { .. },
125+
)
126+
| ast::Expression::InlineExpression(
127+
ast::InlineExpression::TermReference { .. },
128+
)
129+
| ast::Expression::InlineExpression(
130+
ast::InlineExpression::StringLiteral { .. },
131+
) => false,
132+
_ => true,
133+
};
134+
if needs_isolation {
135+
write!(string, "\u{2068}{}\u{2069}", result).expect("Writing succeeded");
136+
} else {
137+
write!(string, "{}", result).expect("Writing succeeded");
138+
};
123139
}
124140
}
125141
}
@@ -175,7 +191,24 @@ impl<'source> ResolveValue<'source> for ast::Pattern<'source> {
175191
}
176192
ast::PatternElement::Placeable(p) => {
177193
let result = p.resolve(scope).to_string();
178-
string.push_str(&result);
194+
let needs_isolation = scope.bundle.use_isolating
195+
&& match p {
196+
ast::Expression::InlineExpression(
197+
ast::InlineExpression::MessageReference { .. },
198+
)
199+
| ast::Expression::InlineExpression(
200+
ast::InlineExpression::TermReference { .. },
201+
)
202+
| ast::Expression::InlineExpression(
203+
ast::InlineExpression::StringLiteral { .. },
204+
) => false,
205+
_ => true,
206+
};
207+
if needs_isolation {
208+
write!(string, "\u{2068}{}\u{2069}", result).expect("Writing succeeded");
209+
} else {
210+
write!(string, "{}", result).expect("Writing succeeded");
211+
};
179212
}
180213
}
181214
}

fluent-bundle/tests/bundle_test.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
mod helpers;
22

3+
use std::collections::HashMap;
4+
35
use self::helpers::{
46
assert_format_no_errors, assert_get_bundle_no_errors, assert_get_resource_from_str_no_errors,
57
};
@@ -66,3 +68,32 @@ bar =
6668
assert_eq!(bundle.has_message("missing"), false);
6769
assert_eq!(bundle.has_message("-missing"), false);
6870
}
71+
72+
#[test]
73+
fn bundle_use_isolating() {
74+
let res = assert_get_resource_from_str_no_errors(
75+
"
76+
foo = Foo
77+
bar = Foo { $name } bar
78+
",
79+
);
80+
let mut bundle = assert_get_bundle_no_errors(&res, None);
81+
82+
// By default `assert_get_bundle_no_errors` sets isolating to false.
83+
bundle.set_use_isolating(true);
84+
85+
assert_format_no_errors(bundle.format("foo", None), "Foo");
86+
let mut args = HashMap::new();
87+
args.insert("name", "John".into());
88+
assert_format_no_errors(
89+
bundle.format("bar", Some(&args)),
90+
"Foo \u{2068}John\u{2069} bar",
91+
);
92+
93+
bundle.set_use_isolating(false);
94+
95+
assert_format_no_errors(bundle.format("foo", None), "Foo");
96+
let mut args = HashMap::new();
97+
args.insert("name", "John".into());
98+
assert_format_no_errors(bundle.format("bar", Some(&args)), "Foo John bar");
99+
}

fluent-bundle/tests/helpers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub fn assert_get_bundle_no_errors<'a, R: Borrow<FluentResource>>(
4949
locale: Option<&str>,
5050
) -> FluentBundle<R> {
5151
let mut bundle = FluentBundle::new(&[locale.unwrap_or("x-testing")]);
52+
bundle.set_use_isolating(false);
5253
bundle
5354
.add_resource(res)
5455
.expect("Failed to add FluentResource to FluentBundle.");

0 commit comments

Comments
 (0)