extern crate locale_config;
extern crate gettext_sys as ffi;
use std::ffi::CString;
use std::ffi::CStr;
use std::os::raw::c_ulong;
mod macros;
pub use macros::*;
mod text_domain;
pub use text_domain::{TextDomain, TextDomainError};
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum LocaleCategory {
LcCType = 0,
LcNumeric = 1,
LcTime = 2,
LcCollate = 3,
LcMonetary = 4,
LcMessages = 5,
LcAll = 6,
LcPaper = 7,
LcName = 8,
LcAddress = 9,
LcTelephone = 10,
LcMeasurement = 11,
LcIdentification = 12,
}
pub fn gettext<T: Into<Vec<u8>>>(s: T) -> String {
unsafe {
CStr::from_ptr(ffi::gettext(CString::new(s).unwrap().as_ptr()))
.to_string_lossy()
.into_owned()
}
}
pub fn dgettext<T: Into<Vec<u8>>>(domain: T, s: T) -> String {
unsafe {
CStr::from_ptr(ffi::dgettext(CString::new(domain).unwrap().as_ptr(), CString::new(s).unwrap().as_ptr()))
.to_string_lossy()
.into_owned()
}
}
pub fn dcgettext<T: Into<Vec<u8>>>(domain: T, s: T, category: LocaleCategory) -> String {
unsafe {
CStr::from_ptr(ffi::dcgettext(CString::new(domain).unwrap().as_ptr(), CString::new(s).unwrap().as_ptr(), category as i32))
.to_string_lossy()
.into_owned()
}
}
pub fn ngettext<T: Into<Vec<u8>>>(singular: T, plural : T, n : u32) -> String {
unsafe {
CStr::from_ptr(ffi::ngettext(CString::new(singular).unwrap().as_ptr(), CString::new(plural).unwrap().as_ptr(), n as c_ulong))
.to_string_lossy()
.into_owned()
}
}
pub fn dngettext<T: Into<Vec<u8>>>(domain: T, singular: T, plural: T, n : u32) -> String {
unsafe {
CStr::from_ptr(ffi::dngettext(CString::new(domain).unwrap().as_ptr(), CString::new(singular).unwrap().as_ptr(), CString::new(plural).unwrap().as_ptr(), n as c_ulong))
.to_string_lossy()
.into_owned()
}
}
pub fn dcngettext<T: Into<Vec<u8>>>(domain: T, singular: T, plural: T, n : u32, category: LocaleCategory) -> String {
unsafe {
CStr::from_ptr(ffi::dcngettext(CString::new(domain).unwrap().as_ptr(), CString::new(singular).unwrap().as_ptr(), CString::new(plural).unwrap().as_ptr(), n as c_ulong, category as i32))
.to_string_lossy()
.into_owned()
}
}
pub fn textdomain<T: Into<Vec<u8>>>(domain: T) -> String {
unsafe {
CStr::from_ptr(ffi::textdomain(CString::new(domain).unwrap().as_ptr()))
.to_string_lossy()
.into_owned()
}
}
pub fn bindtextdomain<T: Into<Vec<u8>>>(domain: T, dir: T) -> String {
unsafe {
CStr::from_ptr(ffi::bindtextdomain(CString::new(domain).unwrap().as_ptr(),
CString::new(dir).unwrap().as_ptr()))
.to_string_lossy()
.into_owned()
}
}
pub fn setlocale<T: Into<Vec<u8>>>(category: LocaleCategory, locale: T) -> Option<String> {
let c = CString::new(locale).unwrap();
unsafe {
let ret = ffi::setlocale(category as i32, c.as_ptr());
if ret.is_null() {
None
} else {
Some(CStr::from_ptr(ret).to_string_lossy().into_owned())
}
}
}
pub fn bind_textdomain_codeset<T: Into<Vec<u8>>>(domain: T, codeset: T) -> String {
unsafe {
CStr::from_ptr(ffi::bind_textdomain_codeset(CString::new(domain).unwrap().as_ptr(),
CString::new(codeset).unwrap().as_ptr()))
.to_string_lossy()
.into_owned()
}
}
static CONTEXT_SEPARATOR: u8 = b'\x04';
fn build_context_id(ctx: &Vec<u8>, s: &Vec<u8>) -> String {
let mut text: Vec<u8> = vec![];
text.extend(ctx.iter().cloned());
text.push(CONTEXT_SEPARATOR);
text.extend(s.iter().cloned());
CString::new(text).unwrap().to_string_lossy().into_owned()
}
pub fn pgettext<T: Into<Vec<u8>>>(ctx: T, s: T) -> String {
let msgid = s.into();
let text = build_context_id(&ctx.into(), &msgid);
let trans = gettext(text);
if trans.contains(CONTEXT_SEPARATOR as char) {
return gettext(msgid);
}
trans
}
pub fn npgettext<T: Into<Vec<u8>>>(ctx: T, singular: T, plural: T, n: u32) -> String {
let ctx = ctx.into();
let singular_msgid = singular.into();
let plural_msgid = plural.into();
let singular_ctx = build_context_id(&ctx, &singular_msgid);
let plural_ctx = build_context_id(&ctx, &plural_msgid);
let trans = ngettext(singular_ctx, plural_ctx, n);
if trans.contains(CONTEXT_SEPARATOR as char) {
return ngettext(singular_msgid, plural_msgid, n);
}
trans
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke_test() {
setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
bindtextdomain("hellorust", "/usr/local/share/locale");
textdomain("hellorust");
assert_eq!("Hello, world!", gettext("Hello, world!"));
}
#[test]
fn plural_test() {
setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
bindtextdomain("hellorust", "/usr/local/share/locale");
textdomain("hellorust");
assert_eq!("Hello, world!", ngettext("Hello, world!", "Hello, worlds!", 1));
assert_eq!("Hello, worlds!", ngettext("Hello, world!", "Hello, worlds!", 2));
}
#[test]
fn context_test() {
setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
bindtextdomain("hellorust", "/usr/local/share/locale");
textdomain("hellorust");
assert_eq!("Hello, world!", pgettext("context", "Hello, world!"));
}
#[test]
fn plural_context_test() {
setlocale(LocaleCategory::LcAll, "en_US.UTF-8");
bindtextdomain("hellorust", "/usr/local/share/locale");
textdomain("hellorust");
assert_eq!("Hello, world!", npgettext("context", "Hello, world!", "Hello, worlds!", 1));
assert_eq!("Hello, worlds!", npgettext("context", "Hello, world!", "Hello, worlds!", 2));
}
}