Merge branch 'master' of https://github.com/Aardwolf-Social/gettext-macros
This commit is contained in:
commit
741a6075aa
3 changed files with 146 additions and 66 deletions
|
@ -56,4 +56,3 @@ pub fn try_format<'a>(
|
|||
}
|
||||
::std::result::Result::Ok(res)
|
||||
}
|
||||
|
||||
|
|
162
src/lib.rs
162
src/lib.rs
|
@ -1,13 +1,20 @@
|
|||
#![feature(proc_macro_hygiene, proc_macro_quote, proc_macro_span, uniform_paths)]
|
||||
|
||||
extern crate proc_macro;
|
||||
use std::{env, io::{BufRead, Write}, fs::{create_dir_all, read, File, OpenOptions}, iter::FromIterator, path::Path, process::Command};
|
||||
use proc_macro::{Delimiter, Literal, Spacing, Punct, TokenStream, TokenTree, quote};
|
||||
use std::{
|
||||
env,
|
||||
fs::{create_dir_all, read, File, OpenOptions},
|
||||
io::{BufRead, Read, Seek, SeekFrom, Write},
|
||||
iter::FromIterator,
|
||||
path::Path,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
fn is(t: &TokenTree, ch: char) -> bool {
|
||||
match t {
|
||||
TokenTree::Punct(p) => p.as_char() == ch,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,31 +43,56 @@ fn trim(t: TokenTree) -> TokenTree {
|
|||
|
||||
#[proc_macro]
|
||||
pub fn i18n(input: TokenStream) -> TokenStream {
|
||||
let span = input.clone().into_iter().next().expect("Expected catalog").span();
|
||||
let span = input
|
||||
.clone()
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("Expected catalog")
|
||||
.span();
|
||||
let mut input = input.into_iter();
|
||||
let catalog = input.clone().take_while(|t| !is(t, ',')).collect::<Vec<_>>();
|
||||
let catalog = input
|
||||
.clone()
|
||||
.take_while(|t| !is(t, ','))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let file = span.source_file().path();
|
||||
let line = span.start().line;
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into())).join("gettext_macros");
|
||||
let domain = read(out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo"))).expect("Coudln't read domain, make sure to call init_i18n! before").lines().next().unwrap().unwrap();
|
||||
let mut pot = OpenOptions::new().append(true).create(true).open(format!("po/{0}/{0}.pot", domain)).expect("Couldn't open .pot file");
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into()))
|
||||
.join("gettext_macros");
|
||||
let domain = read(out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo")))
|
||||
.expect("Coudln't read domain, make sure to call init_i18n! before")
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let mut pot = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(format!("po/{0}/{0}.pot", domain))
|
||||
.expect("Couldn't open .pot file");
|
||||
|
||||
for _ in 0..(catalog.len() + 1) { input.next(); }
|
||||
let pot_reader = read(format!("po/{0}/{0}.pot", domain)).expect("Couldn't read .pot file");
|
||||
let mut pot_lines = pot_reader.lines();
|
||||
for _ in 0..(catalog.len() + 1) {
|
||||
input.next();
|
||||
}
|
||||
let message = trim(input.next().unwrap());
|
||||
let msgid = format!("msgid {}", message);
|
||||
let dont_write = is_empty(&message) || pot_lines.any(|l| l.map(|l| l == msgid).unwrap_or(false));
|
||||
|
||||
let mut contents = String::new();
|
||||
pot.read_to_string(&mut contents).unwrap();
|
||||
pot.seek(SeekFrom::End(0)).unwrap();
|
||||
|
||||
let already_exists = is_empty(&message) || contents.contains(&format!("msgid {}", message));
|
||||
|
||||
let plural = match input.clone().next() {
|
||||
Some(t) => if is(&t, ',') {
|
||||
Some(t) => {
|
||||
if is(&t, ',') {
|
||||
input.next();
|
||||
input.next()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut format_args = vec![];
|
||||
|
@ -94,24 +126,47 @@ pub fn i18n(input: TokenStream) -> TokenStream {
|
|||
String::new()
|
||||
};
|
||||
if let Some(pl) = plural {
|
||||
if !dont_write {
|
||||
pot.write_all(&format!(r#"
|
||||
if !already_exists {
|
||||
pot.write_all(
|
||||
&format!(
|
||||
r#"
|
||||
{}msgid {}
|
||||
msgid_plural {}
|
||||
msgstr[0] ""
|
||||
"#, code_path, message, pl).into_bytes()).expect("Couldn't write message to .pot (plural)");
|
||||
"#,
|
||||
code_path,
|
||||
message,
|
||||
pl
|
||||
)
|
||||
.into_bytes(),
|
||||
)
|
||||
.expect("Couldn't write message to .pot (plural)");
|
||||
}
|
||||
let count = format_args.clone().into_iter().next().expect("Item count should be specified").clone();
|
||||
let count = format_args
|
||||
.clone()
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("Item count should be specified")
|
||||
.clone();
|
||||
res.extend(quote!(
|
||||
.ngettext($message, $pl, $count as u64)
|
||||
))
|
||||
} else {
|
||||
if !dont_write {
|
||||
pot.write_all(&format!(r#"
|
||||
if !already_exists {
|
||||
pot.write_all(
|
||||
&format!(
|
||||
r#"
|
||||
{}msgid {}
|
||||
msgstr ""
|
||||
"#, code_path, message).into_bytes()).expect("Couldn't write message to .pot");
|
||||
"#,
|
||||
code_path,
|
||||
message
|
||||
)
|
||||
.into_bytes(),
|
||||
)
|
||||
.expect("Couldn't write message to .pot");
|
||||
}
|
||||
|
||||
res.extend(quote!(
|
||||
.gettext($message)
|
||||
))
|
||||
|
@ -154,11 +209,13 @@ pub fn init_i18n(input: TokenStream) -> TokenStream {
|
|||
break;
|
||||
}
|
||||
match input.next() {
|
||||
Some(TokenTree::Ident(i)) => { langs.push(i); },
|
||||
_ => panic!("Expected a language identifier")
|
||||
Some(TokenTree::Ident(i)) => {
|
||||
langs.push(i);
|
||||
}
|
||||
_ => panic!("Expected a language identifier"),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => panic!("Expected a language identifier"),
|
||||
}
|
||||
} else {
|
||||
|
@ -167,7 +224,8 @@ pub fn init_i18n(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
// emit file to include
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into())).join("gettext_macros");
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into()))
|
||||
.join("gettext_macros");
|
||||
let out = out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo"));
|
||||
create_dir_all(out_dir).expect("Couldn't create output dir");
|
||||
let mut out = File::create(out).expect("Metadata file couldn't be open");
|
||||
|
@ -178,8 +236,15 @@ pub fn init_i18n(input: TokenStream) -> TokenStream {
|
|||
|
||||
// write base .pot
|
||||
create_dir_all(format!("po/{}", domain)).expect("Couldn't create po dir");
|
||||
let mut pot = OpenOptions::new().write(true).create(true).truncate(true).open(format!("po/{0}/{0}.pot", domain)).expect("Couldn't open .pot file");
|
||||
pot.write_all(&format!(r#"msgid ""
|
||||
let mut pot = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(format!("po/{0}/{0}.pot", domain))
|
||||
.expect("Couldn't open .pot file");
|
||||
pot.write_all(
|
||||
&format!(
|
||||
r#"msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: {}\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
|
@ -192,28 +257,43 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||||
"#, domain).into_bytes()).expect("Couldn't init .pot file");
|
||||
"#,
|
||||
domain
|
||||
)
|
||||
.into_bytes(),
|
||||
)
|
||||
.expect("Couldn't init .pot file");
|
||||
|
||||
quote!()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn i18n_domain(_: TokenStream) -> TokenStream {
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into())).join("gettext_macros");
|
||||
let domain = read(out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo"))).expect("Coudln't read domain, make sure to call init_i18n! before").lines().next().unwrap().unwrap();
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into()))
|
||||
.join("gettext_macros");
|
||||
let domain = read(out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo")))
|
||||
.expect("Coudln't read domain, make sure to call init_i18n! before")
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let tok = TokenTree::Literal(Literal::string(&domain));
|
||||
quote!($tok)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn compile_i18n(_: TokenStream) -> TokenStream {
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into())).join("gettext_macros");
|
||||
let file = read(out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo"))).expect("Coudln't read domain, make sure to call init_i18n! before");
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into()))
|
||||
.join("gettext_macros");
|
||||
let file = read(out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo")))
|
||||
.expect("Coudln't read domain, make sure to call init_i18n! before");
|
||||
let mut lines = file.lines();
|
||||
let domain = lines.next().unwrap().unwrap();
|
||||
let locales = lines.map(|l| l.unwrap()).collect::<Vec<_>>();
|
||||
|
||||
let pot_path = Path::new("po").join(domain.clone()).join(format!("{}.pot", domain));
|
||||
let pot_path = Path::new("po")
|
||||
.join(domain.clone())
|
||||
.join(format!("{}.pot", domain));
|
||||
|
||||
for lang in locales {
|
||||
let po_path = Path::new("po").join(domain.clone()).join(format!("{}.po", lang.clone()));
|
||||
|
@ -281,21 +361,23 @@ pub fn compile_i18n(_: TokenStream) -> TokenStream {
|
|||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn include_i18n(_: TokenStream) -> TokenStream {
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into())).join("gettext_macros");
|
||||
let file = read(out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo"))).expect("Coudln't read domain, make sure to call init_i18n! before");
|
||||
let out_dir = Path::new(&env::var("CARGO_TARGET_DIR").unwrap_or("target/debug".into()))
|
||||
.join("gettext_macros");
|
||||
let file = read(out_dir.join(env::var("CARGO_PKG_NAME").expect("Please build with cargo")))
|
||||
.expect("Coudln't read domain, make sure to call init_i18n! before");
|
||||
let mut lines = file.lines();
|
||||
let domain = TokenTree::Literal(Literal::string(&lines.next().unwrap().unwrap()));
|
||||
let locales = lines
|
||||
.map(Result::unwrap)
|
||||
.map(|l| {
|
||||
let lang = TokenTree::Literal(Literal::string(&l));
|
||||
quote!({
|
||||
::gettext::Catalog::parse(
|
||||
quote!{
|
||||
($lang, ::gettext::Catalog::parse(
|
||||
&include_bytes!(
|
||||
concat!(env!("CARGO_MANIFEST_DIR"), "/translations/", $lang, "/LC_MESSAGES/", $domain, ".mo")
|
||||
)[..]
|
||||
).expect("Error while loading catalog")
|
||||
}, )
|
||||
).expect("Error while loading catalog")),
|
||||
}
|
||||
}).collect::<TokenStream>();
|
||||
|
||||
quote!({
|
||||
|
|
|
@ -7,13 +7,12 @@ init_i18n!("test", fr, en, de, ja);
|
|||
#[test]
|
||||
fn main() {
|
||||
let catalogs = include_i18n!();
|
||||
let cat = &catalogs[0];
|
||||
let cat = &catalogs[0].1;
|
||||
let x = i18n!(cat, "Hello");
|
||||
let b = i18n!(cat, "Singular", "Plural"; 0);
|
||||
println!("{} {}", x, b);
|
||||
println!("{}", i18n!(cat, "Woohoo, it {}"; "works"));
|
||||
println!(i18n_domain!());
|
||||
|
||||
}
|
||||
|
||||
compile_i18n!();
|
||||
|
|
Loading…
Reference in a new issue