Skip to content

Refactor of the code, 0.8 compatibility, zero-copy AST/parsing, Rust 2018 goodies #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 39 commits into from
Jan 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
3d69f32
Initial attempt to write a zero-copy parser
Jul 29, 2018
77c2b38
Migrate resolver to use copy-free parser
Oct 29, 2018
bdbdaad
Switch to elsa
Dec 28, 2018
6e02272
Merge fluent-bundle and fluent and attempt to use ResourceManager via…
Jan 1, 2019
652b623
Revert "Merge fluent-bundle and fluent and attempt to use ResourceMan…
Jan 1, 2019
9a9c3ab
Drop FluentBundle::add_messages
Jan 1, 2019
957942e
Convert simple-app example to use add_resource
Jan 2, 2019
377ea56
Switch to use rental crate to store source string on FluentResource
Jan 5, 2019
d50fc3e
Fix benches for fluent-bundle
Jan 5, 2019
e26dd25
Fix the crate for stable rust
Jan 5, 2019
bbac435
Use annotate-snippets 0.5
Jan 5, 2019
bc57ec6
Use newer serde to simplify the json ast serialization
Jan 7, 2019
2242b29
Add docs for FluentBundle
JohnHeitmann Dec 12, 2018
bf6a75e
Anonymized example. Tiny style tweak.
JohnHeitmann Dec 12, 2018
bd74a4a
Corrected error description for FluentBundle.format
JohnHeitmann Dec 12, 2018
b9f0886
More clarification on the various flavors of format error.
JohnHeitmann Dec 13, 2018
c555be5
Merge fixes
JohnHeitmann Jan 7, 2019
7aef4d1
Minor cleanups in the parser
Jan 7, 2019
5191b37
Speed up the parser by reducing the Pattern logic
Jan 7, 2019
37201d9
Improve FluentBundle docs
JohnHeitmann Jan 8, 2019
bd3319d
A bunch of small cleanups to prepare for fuzzer
Jan 8, 2019
7941054
Move ast::Variant::value to be a Pattern
Jan 8, 2019
8a64bbe
Add a note explaining the deviation from the EBNF
Jan 8, 2019
fb569af
Remove the unnecessary externs
Jan 8, 2019
a2fa843
Improve get_number_literal to report errors on broken numbers
Jan 8, 2019
d629ae2
Various small perf optimizations and pointer overflow prevention
Jan 9, 2019
aebb85d
Some more parser cleanups.
Jan 9, 2019
4956beb
Extra docs for format_message. Clarified parts of error handling, blu…
JohnHeitmann Jan 11, 2019
9b1d55e
Merge branch 'copy-free-resolver' into docs
zbraniecki Jan 16, 2019
442a979
Merge pull request #1 from JohnHeitmann/docs
zbraniecki Jan 16, 2019
be4b1ee
Rename format_message to compound
Jan 16, 2019
37695b7
Remove ResourceManager to a separate PR
Jan 16, 2019
96e4b91
Rename lifetime in resolver
Jan 16, 2019
4083bba
Various minor reviewer feedback
Jan 16, 2019
60357e6
Fix the only case where we attempted to get slice with a pointer incr…
Jan 23, 2019
6ce04db
Apply reviewers feedback
Jan 23, 2019
793b019
Another round of feedback
Jan 23, 2019
a4af23a
Move get_slice to use safe code
Jan 23, 2019
61298f3
Round of feedback
Jan 24, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target/
*/target/
**/*.rs.bk
Cargo.lock
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"fluent",
"fluent-syntax"
"fluent-syntax",
"fluent-bundle",
"fluent-cli"
]
File renamed without changes.
15 changes: 8 additions & 7 deletions fluent/Cargo.toml → fluent-bundle/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
[package]
name = "fluent"
name = "fluent-bundle"
description = """
A localization library designed to unleash the entire expressive power of
A localization system designed to unleash the entire expressive power of
natural language translations.
"""
version = "0.4.3"
edition = "2018"
authors = [
"Zibi Braniecki <gandalf@mozilla.com>",
"Staś Małolepszy <stas@mozilla.com>"
Expand All @@ -17,9 +18,9 @@ keywords = ["localization", "l10n", "i18n", "intl", "internationalization"]
categories = ["localization", "internationalization"]

[dependencies]
clap = "2.32"
fluent-locale = "^0.4.1"
fluent-syntax = "0.1.1"
failure = "0.1"
failure_derive = "0.1"
intl_pluralrules = "1.0"
fluent-syntax = { path = "../fluent-syntax" }
failure = "^0.1"
failure_derive = "^0.1"
intl_pluralrules = "^1.0"
rental = "^0.5.2"
2 changes: 1 addition & 1 deletion fluent/README.md → fluent-bundle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Usage
-----

```rust
use fluent::bundle::FluentBundle;
use fluent_bundle::FluentBundle;

fn main() {
let mut bundle = FluentBundle::new(&["en-US"]);
Expand Down
39 changes: 23 additions & 16 deletions fluent/benches/lib.rs → fluent-bundle/benches/lib.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,70 @@
#![feature(test)]

extern crate fluent;
extern crate fluent_syntax;
extern crate test;

use fluent::bundle::FluentBundle;
use fluent_syntax::{ast, parser::parse};
use fluent_bundle::bundle::FluentBundle;
use fluent_bundle::resource::FluentResource;
use fluent_syntax::ast;
use std::fs::File;
use std::io;
use std::io::Read;
use test::Bencher;

fn read_file(path: &str) -> Result<String, io::Error> {
let mut f = try!(File::open(path));
let mut f = File::open(path)?;
let mut s = String::new();
try!(f.read_to_string(&mut s));
f.read_to_string(&mut s)?;
Ok(s)
}

#[bench]
fn bench_simple_format(b: &mut Bencher) {
let source = read_file("./benches/simple.ftl").expect("Couldn't load file");
let resource = parse(&source).unwrap();
let res = FluentResource::try_new(source).expect("Couldn't parse an FTL source");

let mut ids = Vec::new();

for entry in resource.body {
for entry in &res.ast().body {
match entry {
ast::Entry::Message(ast::Message { id, .. }) => ids.push(id.name),
ast::ResourceEntry::Entry(ast::Entry::Message(ast::Message { id, .. })) => {
ids.push(id.name)
}
_ => continue,
};
}

let mut bundle = FluentBundle::new(&["x-testing"]);
bundle.add_messages(&source).unwrap();
bundle
.add_resource(&res)
.expect("Couldn't add FluentResource to the FluentBundle");

b.iter(|| {
for id in &ids {
bundle.format(id.as_str(), None);
bundle.format(id, None);
}
});
}

#[bench]
fn bench_menubar_format(b: &mut Bencher) {
let source = read_file("./benches/menubar.ftl").expect("Couldn't load file");
let resource = parse(&source).unwrap();
let res = FluentResource::try_new(source).expect("Couldn't parse an FTL source");

let mut ids = Vec::new();

for entry in resource.body {
for entry in &res.ast().body {
match entry {
ast::Entry::Message(ast::Message { id, .. }) => ids.push(id.name),
ast::ResourceEntry::Entry(ast::Entry::Message(ast::Message { id, .. })) => {
ids.push(id.name)
}
_ => continue,
};
}

let mut bundle = FluentBundle::new(&["x-testing"]);
bundle.add_messages(&source).unwrap();
bundle
.add_resource(&res)
.expect("Couldn't add FluentResource to the FluentBundle");

b.iter(|| {
for id in &ids {
Expand All @@ -66,7 +73,7 @@ fn bench_menubar_format(b: &mut Bencher) {
// widgets may only expect attributes and they shouldn't be forced to display a value.
// Here however it doesn't matter because we know for certain that the message for `id`
// exists.
bundle.format_message(id.as_str(), None);
bundle.compound(id, None);
}
});
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
extern crate fluent;

use fluent::bundle::FluentBundle;
use fluent::types::FluentValue;
use fluent_bundle::bundle::FluentBundle;
use fluent_bundle::resource::FluentResource;
use fluent_bundle::types::FluentValue;
use std::collections::HashMap;

fn main() {
let mut bundle = FluentBundle::new(&["en"]);
bundle
.add_messages(
"
let res = FluentResource::try_new(
"
hello-world = Hello { $name }
ref = The previous message says { hello-world }
unread-emails =
{ $emailCount ->
[one] You have { $emailCount } unread email
*[other] You have { $emailCount } unread emails
}
",
)
.unwrap();
"
.to_owned(),
)
.unwrap();
let mut bundle = FluentBundle::new(&["en"]);
bundle.add_resource(&res).unwrap();

let mut args = HashMap::new();
args.insert("name", FluentValue::from("John"));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
extern crate fluent;

use fluent::bundle::FluentBundle;
use fluent::types::FluentValue;
use fluent_bundle::bundle::FluentBundle;
use fluent_bundle::resource::FluentResource;
use fluent_bundle::types::FluentValue;

fn main() {
// We define the resources here so that they outlive
// the bundle.
let res1 = FluentResource::try_new("hello-world = Hey there! { HELLO() }".to_owned()).unwrap();
let res2 =
FluentResource::try_new("meaning-of-life = { MEANING_OF_LIFE(42) }".to_owned()).unwrap();
let res3 = FluentResource::try_new(
"all-your-base = { BASE_OWNERSHIP(hello, ownership: \"us\") }".to_owned(),
)
.unwrap();

let mut bundle = FluentBundle::new(&["en-US"]);

// Test for a simple function that returns a string
Expand Down Expand Up @@ -40,15 +49,9 @@ fn main() {
})
.unwrap();

bundle
.add_messages("hello-world = Hey there! { HELLO() }")
.unwrap();
bundle
.add_messages("meaning-of-life = { MEANING_OF_LIFE(42) }")
.unwrap();
bundle
.add_messages("all-your-base = { BASE_OWNERSHIP(hello, ownership: \"us\") }")
.unwrap();
bundle.add_resource(&res1).unwrap();
bundle.add_resource(&res2).unwrap();
bundle.add_resource(&res3).unwrap();

let value = bundle.format("hello-world", None);
assert_eq!(
Expand Down
10 changes: 10 additions & 0 deletions fluent-bundle/examples/hello.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use fluent_bundle::bundle::FluentBundle;
use fluent_bundle::resource::FluentResource;

fn main() {
let res = FluentResource::try_new("hello-world = Hello, world!".to_owned()).unwrap();
let mut bundle = FluentBundle::new(&["en-US"]);
bundle.add_resource(&res).unwrap();
let (value, _) = bundle.format("hello-world", None).unwrap();
assert_eq!(&value, "Hello, world!");
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
extern crate fluent;

use fluent::bundle::FluentBundle;
use fluent_bundle::bundle::FluentBundle;
use fluent_bundle::resource::FluentResource;

fn main() {
let mut bundle = FluentBundle::new(&["x-testing"]);
bundle
.add_messages(
"
let res = FluentResource::try_new(
"
foo = Foo
foobar = { foo } Bar
bazbar = { baz } Bar
",
)
.unwrap();
"
.to_owned(),
)
.unwrap();

let mut bundle = FluentBundle::new(&["x-testing"]);
bundle.add_resource(&res).unwrap();

match bundle.format("foobar", None) {
Some((value, _)) => println!("{}", value),
Expand Down
22 changes: 11 additions & 11 deletions fluent/examples/selector.rs → fluent-bundle/examples/selector.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
extern crate fluent;

use fluent::bundle::FluentBundle;
use fluent::types::FluentValue;
use fluent_bundle::bundle::FluentBundle;
use fluent_bundle::resource::FluentResource;
use fluent_bundle::types::FluentValue;
use std::collections::HashMap;

fn main() {
let mut bundle = FluentBundle::new(&["x-testing"]);
bundle
.add_messages(
"
let res = FluentResource::try_new(
"
hello-world = Hello {
*[one] World
[two] Moon
Expand All @@ -18,9 +15,12 @@ hello-world2 = Hello { $name ->
*[world] World
[moon] Moon
}
",
)
.unwrap();
"
.to_owned(),
)
.unwrap();
let mut bundle = FluentBundle::new(&["x-testing"]);
bundle.add_resource(&res).unwrap();

match bundle.format("hello-world", None) {
Some((value, _)) => println!("{}", value),
Expand Down
42 changes: 23 additions & 19 deletions fluent/examples/simple.rs → fluent-bundle/examples/simple-app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@
//!
//! If the second argument is omitted, `en-US` locale is used as the
//! default one.
extern crate fluent;
extern crate fluent_locale;

use fluent::bundle::FluentBundle;
use fluent::types::FluentValue;
use fluent_bundle::bundle::FluentBundle;
use fluent_bundle::resource::FluentResource;
use fluent_bundle::types::FluentValue;
use fluent_locale::{negotiate_languages, NegotiationStrategy};
use std::collections::HashMap;
use std::env;
Expand All @@ -37,9 +35,9 @@ use std::str::FromStr;
/// The resource files are stored in
/// `./examples/resources/{locale}` directory.
fn read_file(path: &str) -> Result<String, io::Error> {
let mut f = try!(File::open(path));
let mut f = File::open(path)?;
let mut s = String::new();
try!(f.read_to_string(&mut s));
f.read_to_string(&mut s)?;
Ok(s)
}

Expand Down Expand Up @@ -95,7 +93,10 @@ fn main() {
// 1. Get the command line arguments.
let args: Vec<String> = env::args().collect();

// 2. If the argument length is more than 1,
// 2. Allocate resources.
let mut resources: Vec<FluentResource> = vec![];

// 3. If the argument length is more than 1,
// take the second argument as a comma-separated
// list of requested locales.
//
Expand All @@ -104,38 +105,41 @@ fn main() {
.get(2)
.map_or(vec!["en-US"], |arg| arg.split(",").collect());

// 3. Negotiate it against the avialable ones
// 4. Negotiate it against the avialable ones
let locales = get_app_locales(&requested).expect("Failed to retrieve available locales");

// 4. Create a new Fluent FluentBundle using the
// 5. Create a new Fluent FluentBundle using the
// resolved locales.
let mut bundle = FluentBundle::new(&locales);

// 5. Load the localization resource
// 6. Load the localization resource
for path in L10N_RESOURCES {
let full_path = format!(
"./examples/resources/{locale}/{path}",
locale = locales[0],
path = path
);
let res = read_file(&full_path).unwrap();
// 5.1 Insert the loaded resource into the
// Fluent FluentBundle.
bundle.add_messages(&res).unwrap();
let source = read_file(&full_path).unwrap();
let resource = FluentResource::try_new(source).unwrap();
resources.push(resource);
}

for res in &resources {
bundle.add_resource(res).unwrap();
}

// 6. Check if the input is provided.
// 7. Check if the input is provided.
match args.get(1) {
Some(input) => {
// 6.1. Cast it to a number.
// 7.1. Cast it to a number.
match isize::from_str(&input) {
Ok(i) => {
// 6.2. Construct a map of arguments
// 7.2. Construct a map of arguments
// to format the message.
let mut args = HashMap::new();
args.insert("input", FluentValue::from(i));
args.insert("value", FluentValue::from(collatz(i)));
// 6.3. Format the message.
// 7.3. Format the message.
println!("{}", bundle.format("response-msg", Some(&args)).unwrap().0);
}
Err(err) => {
Expand Down
Loading