Languages
ISO 639 language codes and names in English and in the language itself, two- and three-letter codes read, and the best of your translations picked from Accept-Language.
Version 1.0.0 · 4 functions · 1 crate
Functions
What use languages.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use language, language_valid from languages (Quillo 0.6).
language(code_or_name: str) → anyor an errorA language by its code or name: "de", "deu", "ger", "de-CH", "German" or "Deutsch" give { name, native, iso639_1, iso639_3 } (iso639_1 is null for the languages without a two-letter code). An error when there is none.
language_valid(code: str) → boolWhether code is an ISO 639-1 or 639-3 code (or a 639-2/B one such as "fre"), case ignored.
language_list() → listThe languages with a two-letter code, [{ code, name, native }], sorted by English name: for a language select.
language_negotiate(accept_language: str, supported: list) → stror an errorThe language of supported (two-letter codes, such as your site's translations) that the browser's Accept-Language header likes best, by its q weights: "fr-CH, fr;q=0.9, en;q=0.8" with ["en", "fr"] is "fr". supported[0] when none matches.
Get it
$ quillo install languages # languages.qwer into this folder$ quillo install languages --example # and its example, languages.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/languages.qwer$ curl -O https://quillolang.org/plugins/files/languages.qweThen, in your program, all of it or only what you call:
use languages.qweruse language, language_valid from languages // Quillo 0.6: only theseIt uses this crate, which cargo fetches the first time the plugin is built:
isolang = { version = "2.4", features = ["english_names", "lowercase_names", "local_names", "list_languages"] }Example
// A site in three languages that picks the visitor's from the browser, and a language select.//// quillo run languages.qwe then open http://localhost:8080/hellouse "languages.qwer"let greetings = { en: "Hello", fr: "Bonjour", de: "Hallo" }route GET /hello { let lang = language_negotiate(request.headers["accept-language"] ?? "", ["en", "fr", "de"])? { lang, text: greetings[lang], name: language(lang)?.native }}route GET /negotiate ? { header: str = "fr-CH, fr;q=0.9, en;q=0.8" } = { chosen: language_negotiate(header, ["en", "fr", "de"])? }route GET /lookup = ["de", "deu", "ger", "pt-BR", "German", "Deutsch", "haw"] | map((q) => { q, found: language(q)? })route GET /list = { count: len(language_list()), first: first(language_list()), valid: [language_valid("en"), language_valid("xx")] }$ quillo run languages.qwe$ curl 'localhost:8080/negotiate'$ curl 'localhost:8080/lookup'Source
//! ISO 639 language codes and names, for language pickers, content tags and Accept-Language headers: "fr", "fra" and "fre" all read, with English and native names ("German", "Deutsch").//! version: 1.0.0//!//! [dependencies]//! isolang = { version = "2.4", features = ["english_names", "lowercase_names", "local_names", "list_languages"] }use isolang::Language;use serde_json::{json, Value};/// ISO 639-2/B ("bibliographic") codes, used by libraries and some old systems, and their 639-3 code.const B_CODES: &[(&str, &str)] = &[ ("alb", "sqi"), ("arm", "hye"), ("baq", "eus"), ("bur", "mya"), ("chi", "zho"), ("cze", "ces"), ("dut", "nld"), ("fre", "fra"), ("geo", "kat"), ("ger", "deu"), ("gre", "ell"), ("ice", "isl"), ("mac", "mkd"), ("mao", "mri"), ("may", "msa"), ("per", "fas"), ("rum", "ron"), ("slo", "slk"), ("tib", "bod"), ("wel", "cym"),];fn find(code_or_name: &str) -> Option<Language> { let q = code_or_name.trim(); let l = q.to_lowercase(); match l.len() { 2 => Language::from_639_1(&l), 3 => Language::from_639_3(B_CODES.iter().find(|(b, _)| *b == l).map_or(l.as_str(), |(_, t)| t)), _ => None, } .or_else(|| if l.contains('-') || l.contains('_') { Language::from_locale(&q.replace('-', "_")) } else { None }) .or_else(|| Language::from_name_lowercase(&l)) .or_else(|| { let l = l.clone(); Language::match_autonyms(move |n| n.to_lowercase() == l).next() })}fn info(l: Language) -> Value { json!({ "name": l.to_name(), "native": l.to_autonym(), "iso639_1": l.to_639_1(), "iso639_3": l.to_639_3() })}/// A language by its code or name: "de", "deu", "ger", "de-CH", "German" or "Deutsch" give `{ name, native, iso639_1, iso639_3 }` (`iso639_1` is null for the languages without a two-letter code). An error when there is none.pub fn language(code_or_name: &str) -> Result<Value, String> { find(code_or_name).map(info).ok_or_else(|| format!("no language {:?}", code_or_name))}/// Whether `code` is an ISO 639-1 or 639-3 code (or a 639-2/B one such as "fre"), case ignored.pub fn language_valid(code: &str) -> bool { let l = code.trim().to_lowercase(); (l.len() == 2 || l.len() == 3) && find(&l).is_some()}/// The languages with a two-letter code, `[{ code, name, native }]`, sorted by English name: for a language select.pub fn language_list() -> Vec<Value> { let mut v: Vec<Language> = isolang::languages().filter(|l| l.to_639_1().is_some()).collect(); v.sort_by_key(|l| l.to_name()); v.into_iter().map(|l| json!({ "code": l.to_639_1(), "name": l.to_name(), "native": l.to_autonym() })).collect()}/// The language of `supported` (two-letter codes, such as your site's translations) that the browser's `Accept-Language` header likes best, by its q weights: "fr-CH, fr;q=0.9, en;q=0.8" with ["en", "fr"] is "fr". `supported[0]` when none matches.pub fn language_negotiate(accept_language: &str, supported: Vec<String>) -> Result<String, String> { let first = supported.first().cloned().ok_or("supported is empty")?; let mut wants: Vec<(f64, usize, String)> = accept_language .split(',') .take(50) .enumerate() .filter_map(|(i, part)| { let mut it = part.split(';'); let tag = it.next()?.trim().to_lowercase(); let q = it.find_map(|p| p.trim().strip_prefix("q=").and_then(|v| v.parse::<f64>().ok())).unwrap_or(1.0); (!tag.is_empty() && q > 0.0).then_some((q, i, tag)) }) .collect(); wants.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal).then(a.1.cmp(&b.1))); for (_, _, tag) in wants { if tag == "*" { return Ok(first); } if let Some(s) = supported.iter().find(|s| s.to_lowercase() == tag) { return Ok(s.clone()); } let base = tag.split(['-', '_']).next().unwrap_or(""); if let Some(s) = supported.iter().find(|s| s.to_lowercase().split(['-', '_']).next() == Some(base)) { return Ok(s.clone()); } } Ok(first)}Related
countries.qwer · 1.0.0ISO 3166 countries for forms and addresses: alpha-2, alpha-3 and numeric codes, names in several languages, calling codes, flags, continents, currencies and EU membership.9 views · 7 downloadsCurrencies currencies.qwer · 1.0.0ISO 4217 currencies: names, symbols, minor units and the countries using them, prices formatted, and amounts turned into the cents payment APIs take.6 views · 7 downloadsPhone numbers phone.qwer · 1.0.0Phone numbers checked against Google's libphonenumber data, stored in E.164, shown in international or national form.7 views · 11 downloadsFluent translations fluent.qwer · 1.0.0Project Fluent translations (.ftl): messages formatted by locale with each language's plural forms, selectors on gender or anything else, terms and attributes, fallback from one locale to the next, and translation files checked and compared.8 views · 7 downloadsCard numbers cards.qwer · 1.0.0Payment card numbers checked before they reach a payment provider: Luhn check digit, brand and length, expiry, and the number formatted or masked for receipts.7 views · 7 downloadsEmail addresses email_address.qwer · 1.0.0Email addresses checked against RFC 5322 and split into local part, domain and display name.7 views · 7 downloadsThread 0 posts
On Languages: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.