Skip to content
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

twine extension #7

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ regex = "1"
[dev-dependencies]
criterion = "0.3"
tempfile = "3"

[workspace]
members = [
"twine-demo",
]
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ fn main() {
}
```

3. You need an INI file with your translations. Example with `translations.ini`:
3. You need an INI file with your translations.
Language translations are matched by `two lowercase letter` code (eg: `en`).
Localized language translations are identified by `two lowercase letter` code,
plus `hyphen`, plus `to letter localization` code (eg: `en-gb`).

The next paragraph is an example `translations.ini` file:

```
[app_ruin_the_band]
Expand Down
61 changes: 57 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
//! }
//! ```
//!
//! 3. You need an INI file with your translations. Example with `translations.ini`:
//! 3. You need an INI file with your translations.
//! Language translations are matched by `two lowercase letter` code (eg: `en`).
//! Localized language translations are identified by `two lowercase letter` code,
//! plus `hyphen`, plus `to letter localization` code (eg: `en-gb`).
//!
//! The next paragraph is an example `translations.ini` file:
//!
//! ```text
//! [app_ruin_the_band]
Expand Down Expand Up @@ -54,7 +59,9 @@
//! 4. Now in your project you can use the macro `t!` to translate anything:
//!
//! ```ignore
//! # /// define valid language varients
//! # enum Lang { Fr(&'static str) }
//! # /// desugar the procedural macro call
//! # macro_rules! t {
//! # ($($tokens:tt)+) => {{
//! # }};
Expand Down Expand Up @@ -219,6 +226,16 @@ impl fmt::Display for TwineFormatter {
write!(
f,
r#"

"#,
)?;

write!(
f,
r#"
// i18n.rs

/// Create translation strings for supported language varients.
#[macro_export]
macro_rules! t {{
"#,
Expand Down Expand Up @@ -264,7 +281,9 @@ impl fmt::Display for TwineFormatter {
write!(
f,
r#"
#[derive(Debug, Clone, Copy, PartialEq, Hash)]

/// Valid language variants.
#[derive(Clone, Copy, Hash, Debug, PartialEq)]
#[allow(dead_code)]
pub enum Lang {{
"#,
Expand All @@ -282,9 +301,10 @@ impl fmt::Display for TwineFormatter {
write!(
f,
r#"
/// variant {}
{}(&'static str),
"#,
lang,
lang, lang
)?;
}

Expand All @@ -295,6 +315,7 @@ impl fmt::Display for TwineFormatter {
}}

impl Lang {{
/// Array with known language identifier.
pub fn all_languages() -> &'static [&'static Lang] {{
&[
"#,
Expand Down Expand Up @@ -325,6 +346,38 @@ impl fmt::Display for TwineFormatter {
"#,
)?;

// implent default for `Lang`
// the fist in the sorted list should be fine
write!(
f,
r#"

impl Default for Lang {{
"#,
)?;
f.indent(1);

let mut sorted_languages: Vec<_> = all_languages.iter().collect();
sorted_languages.sort_unstable();

let (default_lang, default_region) = sorted_languages[0];
Comment on lines +361 to +363
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're still taking the first language in alphabetical order. This is not good.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeh,
didn't go into it. Actually i might just get rid of a default branch. User must choose explicitely for the language to choose.
I might implement something in the twine-demo code ....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me

write!(
f,
r#"
fn default() -> Self {{ Lang::{}({:?}) }}
"#,
default_lang,
default_region.as_deref().unwrap_or(""),
)?;
f.dedent(1);

write!(
f,
r#"
}}
"#,
)?;

#[cfg(feature = "serde")]
{
let mut all_regions: Vec<_> = all_languages
Expand Down Expand Up @@ -450,7 +503,7 @@ impl TwineFormatter {
impl<'de> de::Visitor<'de> for LangVisitor {{
type Value = Lang;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {{
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {{
formatter.write_str("expected string")
}}

Expand Down
2 changes: 2 additions & 0 deletions tests/test-crate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ twine = { path = "../..", features = ["serde"] }
[dependencies]
serde = { version = "1" }
serde_json = "1"

[workspace]
21 changes: 21 additions & 0 deletions twine-demo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "twine-demo"
version = "0.1.0"
authors = ["Ralf Zerres <[email protected]>"]
edition = "2018"
build = "build.rs"

[dependencies]
serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1.0" }
substring = "1.4.5"
tracing = "0.1.25"
tracing-subscriber = "0.2.17"
twine = { version = "^0.3", features = ["serde"] }

[build-dependencies]
twine = { version = "^0.3", features = ["serde"] }

[[bin]]
name = "twine-demo"
path = "src/main.rs"
6 changes: 6 additions & 0 deletions twine-demo/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use twine::build_translations;

fn main() {
println!("cargo:rerun-if-changed=build.rs");
build_translations(&["./src/i18n/localization.ini"], "i18n.rs").unwrap();
}
59 changes: 59 additions & 0 deletions twine-demo/data/demo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"_comment1": "The colour structure is an array of colour elements",
"colour": [
{
"colour_name": "black",
"category": "hue",
"colour_type": "Primary",
"code": {
"rgba": [255,255,255,1],
"hex": "000"
}
},
{
"colour_name": "white",
"category": "value",
"colour_type": "Primary",
"code": {
"rgba": [0,0,0,1],
"hex": "FFF"
}
},
{
"colour_name": "red",
"category": "hue",
"colour_type": "Primary",
"code": {
"rgba": [255,0,0,1],
"hex": "FF0"
}
},
{
"colour_name": "blue",
"category": "hue",
"colour_type": "Primary",
"code": {
"rgba": [0,0,255,1],
"hex": "00F"
}
},
{
"colour_name": "yellow",
"category": "hue",
"colour_type": "Primary",
"code": {
"rgba": [255,255,0,1],
"hex": "FF0"
}
},
{
"colour_name": "green",
"category": "hue",
"colour_type": "Secondary",
"code": {
"rgba": [0,255,0,1],
"hex": "0F0"
}
}
]
}
39 changes: 39 additions & 0 deletions twine-demo/src/i18n/localization.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[clock_drift]
de = Uhrzeit ist eventuell rückwärts gestellt worden
en = Clock may have gone backwards
[err_lang_not_found]
de = Konnte Sprachkode nicht auslesen
en = Can not read the language code
[err_import_colours]
de = Import der Farbwert fehlgeschlagen
en = Error importing coulour values
[err_colour_not_found]
de = Farbwert wurde nicht gefunden
en = coulour value not found
[lang_code]
de = Sprachkode
en = language code
[main_started]
de = Programmlogik starten
en = Program logic started
[main_finished]
de = Programmlogik beendet
en = Program logic finished
[import_colours]
de = Import der Farbwerte
en = import coulour values
[import_not_well_formed]
de = JSON hat nicht die erwartete Struktur
en = JSON was not well-formatted
[import_started]
de = Import aus einer Datei
en = Import from a file
[import_finished]
de = Import aus einer Datei beendet
en = Import from a file finished
[state_started]
de = gestartet
en = started
[state_finished]
de = beended
en = finished
59 changes: 59 additions & 0 deletions twine-demo/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]

use std::env;
use std::time::SystemTime;
use tracing::{trace, Level};
use tracing_subscriber::fmt;

// get the macro (t!) accessing the internationalization strings
include!(concat!(env!("OUT_DIR"), "/i18n.rs"));

pub mod services;

use crate::services::imports::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
// initialize the tracing subsystem
let span = tracing::span!(Level::TRACE, "wip_twine");
let _enter = span.enter();
let collector = fmt::Subscriber::builder()
.with_max_level(tracing::Level::TRACE)
.finish();

// start tracing thread
tracing::subscriber::with_default(collector, || {
// include localization strings
let lang = Lang::De("de");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it weird that you are adding a Default impl but you don't seem to use it anywhere.

May I ask, do you have an actual use case where you need Lang::default()? Can you show me or explain

let mut state = t!(state_started => lang);
let mut res = t!(lang_code => lang);

// localized feedback
let time_start = SystemTime::now();
trace!(target: "twine-demo", state = ?state,
res = ? res, time = ?time_start);

match json_import::read_colours("data/demo.json") {
Ok(colours) => {
println!("We got {} colour elements", colours.colour.iter().count());
for (pos, e) in colours.colour.iter().enumerate() {
println!("Element {}: colour name={:?}, rgba-value={:?}", pos, e.colour_name, e.code.rgba);
}
},
Err(e) => {
println!("Error: {}!", e);
res = t!(err_import_colours => lang);
println!("Error: {}!", res);
},
}

state = t!(state_finished => lang);
res = t!(import_colours => lang);

// localized feedback
let time_end = SystemTime::now();
let duration = time_end.duration_since(time_start);
trace!(target: "twine-demo", process = ?res, state = ?state, duration = ?duration);
});

Ok(())
}
59 changes: 59 additions & 0 deletions twine-demo/src/services/imports/json_import.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use serde::Deserialize;
use serde_json::Result;
use std::{
fs::File,
io::BufReader,
path::Path,
};

// Valid color types
#[derive(Debug, Deserialize, PartialEq)]
pub enum Type {
/// Primery colour
Primary,
/// Secondary colour
Secondary,
}

impl Default for Type {
fn default() -> Self { Type::Primary }
}

// Color codes structure
#[derive(Debug, Deserialize, PartialEq)]
pub struct Code {
/// Color code as an rgba array
pub rgba: Vec<u8>,
/// Color code as a hex value
pub hex: String,
}

// The colour structure
#[derive(Debug, Deserialize, PartialEq)]
pub struct Colour {
pub colour_name: String,
pub category: String,
pub colour_type: Type,
pub code: Code,
}

// The colours structure
#[derive(Debug, Deserialize, PartialEq)]
pub struct Colours {
pub colour: Vec<Colour>,
}

pub fn read_colours<P>(path: P) -> Result<Colours>
where
P: AsRef<Path>,
{
// Open the file in read-only mode with buffer
let file = File::open(path).unwrap();
let reader = BufReader::new(file);

// Read the JSON contents of the file as an instance of `Colours`.
let colours: Colours = serde_json::from_reader(reader)?;

// Return the `Colours` structure
Ok(colours)
}
Loading