Skip to content

Commit

Permalink
Merge pull request #66 from urkle/feat-split-carg-i18n-to-separate-crate
Browse files Browse the repository at this point in the history
Feat split carg i18n to separate crate
  • Loading branch information
huacnlee authored Nov 14, 2023
2 parents c269c7c + 7e8b673 commit 470d99b
Show file tree
Hide file tree
Showing 17 changed files with 174 additions and 119 deletions.
20 changes: 3 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ authors = ["Jason Lee <[email protected]>"]
build = "build.rs"
categories = ["localization", "internationalization"]
description = "Rust I18n is use Rust codegen for load YAML file storage translations on compile time, and give you a t! macro for simply get translation texts."
edition = "2018"
edition = "2021"
exclude = ["crates", "tests"]
keywords = ["i18n", "yml", "localization", "internationalization"]
license = "MIT"
Expand All @@ -13,25 +13,15 @@ repository = "https://github.com/longbridgeapp/rust-i18n"
version = "2.2.1"

[dependencies]
anyhow = {version = "1", optional = true}
clap = {version = "2.32", optional = true}
itertools = {version = "0.10.3", optional = true}
once_cell = "1.10.0"
quote = {version = "1", optional = true}
rust-i18n-extract = {path = "./crates/extract", version = "2.1.0", optional = true}
rust-i18n-support = {path = "./crates/support", version = "2.1.0"}
rust-i18n-macro = {path = "./crates/macro", version = "2.1.0"}
serde = "1"
serde_derive = "1"
toml = "0.7.4"

[dev-dependencies]
foo = {path = "examples/foo"}
criterion = "0.5"
lazy_static = "1"

[features]
default = ["rust-i18n-extract", "clap", "anyhow", "quote", "itertools"]
serde_yaml = "0.8"

[build-dependencies]
globwalk = "0.8.1"
Expand All @@ -41,13 +31,9 @@ regex = "1"
name = "app"
test = true

[[bin]]
name = "cargo-i18n"
path = "src/main.rs"
required-features = ["default"]

[workspace]
members = [
"crates/cli",
"crates/extract",
"crates/support",
"crates/macro",
Expand Down
49 changes: 39 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,32 @@ rust-i18n = "2"

Load macro and init translations in `lib.rs` or `main.rs`:

```rs
```rust,no_run
// Load I18n macro, for allow you use `t!` macro in anywhere.
#[macro_use]
extern crate rust_i18n;

# fn main() {
# fn v1() {
// Init translations for current crate.
i18n!("locales");
# }
# fn v2() {
// Or just use `i18n!`, default locales path is: "locales" in current crate.
i18n!();
# }
# fn v3() {
// Config fallback missing translations to "en" locale.
// Use `fallback` option to set fallback locale.
i18n!("locales", fallback = "en");
# }
# }
```

Or you can import by use directly:

```rs
```rust,no_run
// You must import in each files when you wants use `t!` macro.
use rust_i18n::t;
Expand Down Expand Up @@ -149,13 +156,16 @@ This is useful when you use [GitHub Copilot](https://github.com/features/copilot

Import the `t!` macro from this crate into your current scope:

```rs
```rust,no_run
use rust_i18n::t;
```

Then, simply use it wherever a localized string is needed:

```rs
```rust,no_run
# fn _rust_i18n_translate(locale: &str, key: &str) -> String { todo!() }
# fn main() {
use rust_i18n::t;
t!("hello");
// => "Hello world"
Expand All @@ -173,13 +183,14 @@ t!("messages.hello", locale = "zh-CN", name = "Jason", count = 2);
t!("messages.hello", locale = "zh-CN", "name" => "Jason", "count" => 3 + 2);
// => "你好,Jason (5)"
# }
```

### Current Locale

You can use `rust_i18n::set_locale` to set the global locale at runtime, so that you don't have to specify the locale on each `t!` invocation.

```rs
```rust
rust_i18n::set_locale("zh-CN");
let locale = rust_i18n::locale();
Expand All @@ -192,7 +203,17 @@ Since v2.0.0 rust-i18n support extend backend for cusomize your translation impl

For example, you can use HTTP API for load translations from remote server:

```rs
```rust,no_run
# pub mod reqwest {
# pub mod blocking {
# pub struct Response;
# impl Response {
# pub fn text(&self) -> Result<String, Box<dyn std::error::Error>> { todo!() }
# }
# pub fn get(_url: &str) -> Result<Response, Box<dyn std::error::Error>> { todo!() }
# }
# }
# use std::collections::HashMap;
use rust_i18n::Backend;
pub struct RemoteI18n {
Expand All @@ -213,20 +234,28 @@ impl RemoteI18n {
impl Backend for RemoteI18n {
fn available_locales(&self) -> Vec<&str> {
return self.trs.keys().collect();
return self.trs.keys().map(|k| k.as_str()).collect();
}
fn translate(&self, locale: &str, key: &str) -> Option<&str> {
// Write your own lookup logic here.
// For example load from database
return self.trs.get(locale)?.get(key);
return self.trs.get(locale)?.get(key).map(|k| k.as_str());
}
}
```

Now you can init rust_i18n by extend your own backend:

```rs
```rust,no_run
# struct RemoteI18n;
# impl RemoteI18n {
# fn new() -> Self { todo!() }
# }
# impl rust_i18n::Backend for RemoteI18n {
# fn available_locales(&self) -> Vec<&str> { todo!() }
# fn translate(&self, locale: &str, key: &str) -> Option<&str> { todo!() }
# }
rust_i18n::i18n!("locales", backend = RemoteI18n::new());
```

Expand Down
21 changes: 21 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
description = "cargo-i18n tool for the rust-i18n crate."
edition = "2021"
license = "MIT"
name = "rust-i18n-cli"
readme = "../../README.md"
repository = "https://github.com/longbridgeapp/rust-i18n"
version = "2.1.0"

[dependencies]
anyhow = "1"
clap = { version = "4.1.14", features = ["derive"] }
itertools = "0.11.0"
rust-i18n-support = { path = "../support", version = "2.1.0" }
rust-i18n-extract = { path = "../extract", version = "2.1.0" }
serde = { version = "1", features = ["derive"] }
toml = "0.7.4"

[[bin]]
name = "cargo-i18n"
path = "src/main.rs"
3 changes: 2 additions & 1 deletion src/config.rs → crates/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! See `Manifest::from_slice`.
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::fs;
use std::io;
use std::io::Read;
Expand Down Expand Up @@ -138,7 +139,7 @@ fn test_load_default() {
#[test]
fn test_load() {
let workdir = Path::new(env!["CARGO_MANIFEST_DIR"]);
let cargo_root = workdir.join("examples/foo");
let cargo_root = workdir.join("../../examples/foo");

let cfg = load(&cargo_root).unwrap();
assert_eq!(cfg.default_locale, "en");
Expand Down
62 changes: 62 additions & 0 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use anyhow::Error;
use clap::{Args, Parser};

use std::{collections::HashMap, path::Path};

use rust_i18n_extract::{extractor, generator, iter};
mod config;

#[derive(Parser)]
#[command(name = "cargo")]
#[command(bin_name = "cargo")]
enum CargoCli {
I18n(I18nArgs),
}

#[derive(Args)]
#[command(author, version)]
// #[command(propagate_version = true)]
/// Rust I18n command to help you extract all untranslated texts from source code.
///
/// It will iterate all Rust files in the source directory and extract all untranslated texts
/// that used `t!` macro.
/// Then it will generate a YAML file and merge with the existing translations.
///
/// https://github.com/longbridgeapp/rust-i18n
struct I18nArgs {
/// Extract all untranslated I18n texts from source code
#[arg(default_value = "./")]
source: Option<String>,
}

fn main() -> Result<(), Error> {
let CargoCli::I18n(args) = CargoCli::parse();

let mut results = HashMap::new();

let source_path = args.source.expect("Missing source path");

let cfg = config::load(std::path::Path::new(&source_path))?;

iter::iter_crate(&source_path, |path, source| {
extractor::extract(&mut results, path, source)
})?;

let mut messages: Vec<_> = results.values().collect();
messages.sort_by_key(|m| m.index);

let mut has_error = false;

let output_path = Path::new(&source_path).join(&cfg.load_path);

let result = generator::generate(&output_path, &cfg.available_locales, messages.clone());
if result.is_err() {
has_error = true;
}

if has_error {
std::process::exit(1);
}

Ok(())
}
3 changes: 3 additions & 0 deletions crates/macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ serde_json = "1"
serde_yaml = "0.8"
syn = "2.0.18"

[dev-dependencies]
rust-i18n = { path = "../..", version = "*" }

[lib]
proc-macro = true
18 changes: 16 additions & 2 deletions crates/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,17 @@ impl Args {
impl syn::parse::Parse for Args {
/// Parse macro arguments.
///
/// ```ignore
/// ```no_run
/// # use rust_i18n::i18n;
/// # fn v1() {
/// i18n!();
/// # }
/// # fn v2() {
/// i18n!("locales");
/// # }
/// # fn v3() {
/// i18n!("locales", fallback = "en");
/// # }
/// ```
///
/// Ref: https://docs.rs/syn/latest/syn/parse/index.html
Expand Down Expand Up @@ -81,10 +88,17 @@ impl syn::parse::Parse for Args {
///
/// Attribute `fallback` for set the fallback locale, if present `t` macro will use it as the fallback locale.
///
/// ```ignore
/// ```no_run
/// # use rust_i18n::i18n;
/// # fn v1() {
/// i18n!();
/// # }
/// # fn v2() {
/// i18n!("locales");
/// # }
/// # fn v3() {
/// i18n!("locales", fallback = "en");
/// # }
/// ```
#[proc_macro]
pub fn i18n(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
Expand Down
13 changes: 8 additions & 5 deletions crates/support/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@ impl SimpleBackend {

/// Add more translations for the given locale.
///
/// ```ignore
/// let trs = HashMap::<String, String>::new();
/// trs.insert("hello".into(), "Hello".into());
/// trs.insert("foo".into(), "Foo bar".into());
/// backend.add_translations("en", &data);
/// ```no_run
/// # use std::collections::HashMap;
/// # use rust_i18n_support::SimpleBackend;
/// # let mut backend = SimpleBackend::new();
/// let mut trs = HashMap::<&str, &str>::new();
/// trs.insert("hello", "Hello");
/// trs.insert("foo", "Foo bar");
/// backend.add_translations("en", &trs);
/// ```
pub fn add_translations(&mut self, locale: &str, data: &HashMap<&str, &str>) {
let data = data
Expand Down
2 changes: 1 addition & 1 deletion examples/app-load-path/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rust-i18n = { path = "../../../rust-i18n" }
rust-i18n = { path = "../.." }
2 changes: 1 addition & 1 deletion examples/app-workspace/app1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ version = "0.1.0"

[dependencies]
example-base = { path = "../crates/example-base" }
rust-i18n = { path = "../../../../rust-i18n" }
rust-i18n = { path = "../../.." }
2 changes: 1 addition & 1 deletion examples/app-workspace/crates/example-base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rust-i18n = { path = "../../../../../rust-i18n" }
rust-i18n = { path = "../../../.." }
2 changes: 1 addition & 1 deletion examples/foo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rust-i18n = { path = "../../../rust-i18n" }
rust-i18n = { path = "../.." }

[package.metadata.i18n]
available-locales = ["en", "zh-CN"]
Expand Down
2 changes: 1 addition & 1 deletion examples/share-locales-in-workspace/my-app1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ name = "my-app1"
version = "0.1.0"

[dependencies]
rust-i18n = { path = "../../../../rust-i18n" }
rust-i18n = { path = "../../.." }
my-i18n.workspace = true
2 changes: 1 addition & 1 deletion examples/share-locales-in-workspace/my-app2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ name = "my-app2"
version = "0.1.0"

[dependencies]
rust-i18n = { path = "../../../../rust-i18n" }
rust-i18n = { path = "../../.." }
my-i18n.workspace = true
2 changes: 1 addition & 1 deletion examples/share-locales-in-workspace/my-i18n/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ name = "my-i18n"
version = "0.1.0"

[dependencies]
rust-i18n = { path = "../../../../rust-i18n" }
rust-i18n = { path = "../../.." }
Loading

0 comments on commit 470d99b

Please sign in to comment.