Commit 0a056b87 authored by Federico Mena Quintero's avatar Federico Mena Quintero

Merge branch 'system-language' into 'master'

#256 - System language

Closes #256

See merge request !136
parents 22bbadb3 ecf3a786
Pipeline #34322 failed with stages
in 38 minutes
......@@ -478,6 +478,25 @@ name = "itoa"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "language-tags"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.1.0"
......@@ -496,6 +515,17 @@ name = "libm"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "locale_config"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.5"
......@@ -803,6 +833,18 @@ dependencies = [
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "1.0.5"
......@@ -815,6 +857,14 @@ dependencies = [
"utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.6.2"
......@@ -837,8 +887,10 @@ dependencies = [
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"locale_config 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"nalgebra 0.16.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -1095,6 +1147,11 @@ dependencies = [
"winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.6"
......@@ -1104,6 +1161,11 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
......@@ -1174,9 +1236,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450"
"checksum itertools-num 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "83ca7b70b838f2e34bc6c2f367a1ed1cfe34fb82464adecadd31cdcc7da882fc"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7"
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
"checksum libm 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "03c0bb6d5ce1b5cc6fd0578ec1cbc18c9d88b5b591a5c7c1d6c6175e266a0819"
"checksum locale_config 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "14fbee0e39bc2dd6a2427c4fdea66e9826cc1fd09b0a0b7550359f5f6efe1dab"
"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum matrixmultiply 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cac1a66eab356036af85ea093101a14223dc6e3f4c02a59b7d572e5b93270bf7"
......@@ -1214,7 +1280,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
"checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341"
"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d"
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7"
......@@ -1250,7 +1318,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "af464bc7be7b785c7ac72e266a6b67c4c9070155606f51655a650a6686204e35"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
......@@ -32,6 +32,8 @@ cssparser = "0.24"
lazy_static = "1.0.0"
phf = "0.7.21"
float-cmp = "0.4.0"
language-tags = "0.2.2"
locale_config = "*" # recommended explicitly by locale_config's README.md
nalgebra = "0.16"
num-traits = "0.2"
owning_ref = "0.3"
......
use error::*;
use std::marker::PhantomData;
#[allow(unused_imports, deprecated)]
use std::ascii::AsciiExt;
use std::str::FromStr;
use glib;
use itertools::{FoldWhile, Itertools};
use language_tags::LanguageTag;
use locale_config::{LanguageRange, Locale};
use error::*;
use parsers::ParseError;
// No extensions at the moment.
static IMPLEMENTED_EXTENSIONS: &[&str] = &[];
......@@ -61,35 +68,103 @@ impl RequiredFeatures {
}
#[derive(Debug, PartialEq)]
pub struct SystemLanguage<'a>(pub bool, pub PhantomData<&'a i8>);
impl<'a> SystemLanguage<'a> {
// Parse a systemLanguage attribute
// http://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute
pub fn from_attribute(
s: &str,
system_languages: &[String],
) -> Result<SystemLanguage<'a>, ValueErrorKind> {
Ok(SystemLanguage(
s.split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.any(|l| {
system_languages.iter().any(|sl| {
if sl.eq_ignore_ascii_case(l) {
return true;
}
pub struct SystemLanguage(pub bool);
if let Some(offset) = l.find('-') {
return sl.eq_ignore_ascii_case(&l[..offset]);
impl SystemLanguage {
/// Parse a `systemLanguage` attribute and match it against a given `Locale`
///
/// The [`systemLanguage`] conditional attribute is a
/// comma-separated list of [BCP47] Language Tags. This function
/// parses the attribute and matches the result against a given
/// `locale`. If there is a match, i.e. if the given locale
/// supports one of the languages listed in the `systemLanguage`
/// attribute, then the `SystemLanguage.0` will be `true`;
/// otherwise it will be `false`.
///
/// Normally, calling code will pass `&Locale::current()` for the
/// `locale` attribute; this is the user's current locale.
///
/// [`systemLanguage`]: https://www.w3.org/TR/SVG/struct.html#ConditionalProcessingSystemLanguageAttribute
/// [BCP47]: http://www.ietf.org/rfc/bcp/bcp47.txt
pub fn from_attribute(s: &str, locale: &Locale) -> Result<SystemLanguage, ValueErrorKind> {
s.split(',')
.map(LanguageTag::from_str)
.fold_while(
// start with no match
Ok(SystemLanguage(false)),
// The accumulator is Result<SystemLanguage, ValueErrorKind>
|acc, tag_result| match tag_result {
Ok(language_tag) => {
let have_match = acc.unwrap().0;
if have_match {
FoldWhile::Continue(Ok(SystemLanguage(have_match)))
} else {
locale_accepts_language_tag(locale, &language_tag)
.map(|matches| FoldWhile::Continue(Ok(SystemLanguage(matches))))
.unwrap_or_else(|e| FoldWhile::Done(Err(e)))
}
}
false
})
}),
PhantomData,
))
Err(e) => FoldWhile::Done(Err(ValueErrorKind::Parse(ParseError::new(
&format!("invalid language tag: \"{}\"", e),
)))),
},
)
.into_inner()
}
}
/// Gets the user's preferred locale from the environment and
/// translates it to a `Locale` with `LanguageRange` fallbacks.
///
/// The `Locale::current()` call only contemplates a single language,
/// but glib is smarter, and `g_get_langauge_names()` can provide
/// fallbacks, for example, when LC_MESSAGES="en_US.UTF-8:de" (USA
/// English and German). This function converts the output of
/// `g_get_language_names()` into a `Locale` with appropriate
/// fallbacks.
pub fn locale_from_environment() -> Result<Locale, String> {
let mut locale = Locale::invariant();
for name in glib::get_language_names() {
let range = LanguageRange::from_unix(&name).map_err(|e| format!("{}", e))?;
locale.add(&range);
}
Ok(locale)
}
fn locale_accepts_language_tag(
locale: &Locale,
language_tag: &LanguageTag,
) -> Result<bool, ValueErrorKind> {
for locale_range in locale.tags_for("messages") {
if locale_range == LanguageRange::invariant() {
continue;
}
let str_locale_range = locale_range.as_ref();
let locale_tag = LanguageTag::from_str(str_locale_range).map_err(|e| {
ValueErrorKind::Parse(ParseError::new(&format!(
"invalid language tag \"{}\" in locale: {}",
str_locale_range, e
)))
})?;
if !locale_tag.is_language_range() {
return Err(ValueErrorKind::Value(format!(
"language tag \"{}\" is not a language range",
locale_tag
)));
}
if locale_tag.matches(language_tag) {
return Ok(true);
}
}
Ok(false)
}
#[cfg(test)]
......@@ -135,41 +210,50 @@ mod tests {
#[test]
fn system_language() {
let system_languages = vec![String::from("de"), String::from("en_US")];
let user_prefers = Locale::new("de,en-US").unwrap();
assert!(SystemLanguage::from_attribute("", &user_prefers).is_err());
assert!(SystemLanguage::from_attribute("12345", &user_prefers).is_err());
assert_eq!(
SystemLanguage::from_attribute("fr", &user_prefers),
Ok(SystemLanguage(false))
);
assert_eq!(
SystemLanguage::from_attribute("", &system_languages),
Ok(SystemLanguage(false, PhantomData))
SystemLanguage::from_attribute("en", &user_prefers),
Ok(SystemLanguage(false))
);
assert_eq!(
SystemLanguage::from_attribute("fr", &system_languages),
Ok(SystemLanguage(false, PhantomData))
SystemLanguage::from_attribute("de", &user_prefers),
Ok(SystemLanguage(true))
);
assert_eq!(
SystemLanguage::from_attribute("de", &system_languages),
Ok(SystemLanguage(true, PhantomData))
SystemLanguage::from_attribute("en-US", &user_prefers),
Ok(SystemLanguage(true))
);
assert_eq!(
SystemLanguage::from_attribute("en_US", &system_languages),
Ok(SystemLanguage(true, PhantomData))
SystemLanguage::from_attribute("en-GB", &user_prefers),
Ok(SystemLanguage(false))
);
assert_eq!(
SystemLanguage::from_attribute("DE", &system_languages),
Ok(SystemLanguage(true, PhantomData))
SystemLanguage::from_attribute("DE", &user_prefers),
Ok(SystemLanguage(true))
);
assert_eq!(
SystemLanguage::from_attribute("de-LU", &system_languages),
Ok(SystemLanguage(true, PhantomData))
SystemLanguage::from_attribute("de-LU", &user_prefers),
Ok(SystemLanguage(true))
);
assert_eq!(
SystemLanguage::from_attribute("fr, de", &system_languages),
Ok(SystemLanguage(true, PhantomData))
SystemLanguage::from_attribute("fr, de", &user_prefers),
Ok(SystemLanguage(true))
);
}
}
......@@ -10,7 +10,9 @@ extern crate gdk_pixbuf;
extern crate glib;
extern crate glib_sys;
extern crate itertools;
extern crate language_tags;
extern crate libc;
extern crate locale_config;
extern crate nalgebra;
extern crate num_traits;
extern crate owning_ref;
......@@ -28,11 +30,7 @@ extern crate downcast_rs;
pub use color::{rsvg_css_parse_color, ColorKind, ColorSpec};
pub use css::{
rsvg_css_parse_into_handle,
rsvg_css_styles_free,
rsvg_css_styles_new,
};
pub use css::{rsvg_css_parse_into_handle, rsvg_css_styles_free, rsvg_css_styles_new};
pub use defs::{rsvg_defs_free, rsvg_defs_lookup, rsvg_defs_new};
......
use cairo::{Matrix, MatrixTrait};
use downcast_rs::*;
use glib;
use glib::translate::*;
use glib_sys;
use std::cell::{Cell, Ref, RefCell};
......@@ -8,7 +7,7 @@ use std::ptr;
use std::rc::{Rc, Weak};
use attributes::Attribute;
use cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
use cond::{locale_from_environment, RequiredExtensions, RequiredFeatures, SystemLanguage};
use drawing_ctx::DrawingCtx;
use error::*;
use handle::{self, RsvgHandle};
......@@ -390,8 +389,11 @@ impl Node {
}
Attribute::SystemLanguage if cond => {
cond = SystemLanguage::from_attribute(value, &glib::get_language_names())
.map(|SystemLanguage(res, _)| res)?;
cond = SystemLanguage::from_attribute(
value,
&(locale_from_environment().map_err(|e| ValueErrorKind::Value(e))?),
)
.map(|SystemLanguage(res)| res)?;
}
_ => {}
......
......@@ -380,7 +380,7 @@ main (int argc, char **argv)
int result;
/* For systemLanguage attribute tests */
g_setenv ("LANGUAGE", "de:en_US", TRUE);
g_setenv ("LC_ALL", "de:en_US:en", TRUE);
g_test_init (&argc, &argv, NULL);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment