1#![doc = include_str!("../README.md")]
2#[cfg(any(doc, feature = "build"))]
3mod build;
4mod error;
5mod l10n_bundle;
6mod l10n_language_vec;
7mod structured;
8
9pub use error::L10nError;
10
11#[cfg(all(test, feature = "build"))]
12mod tests;
13
14#[cfg(any(doc, feature = "build"))]
15pub use build::{
16 BuildError, BuildOptions, FtlOutputOptions, LintLevel, build_from_locales_folder,
17 try_build_from_locales_folder,
18};
19
20#[cfg(feature = "bench-internals")]
23pub use build::bench_internals;
24
25pub mod prelude {
26 pub use crate::error::L10nError;
27 pub use crate::l10n_bundle::L10nBundle;
28 pub use crate::l10n_language_vec::L10nLanguageVec;
29 pub use crate::structured::{ElementGap, Segment};
30 pub use fluent_bundle::{FluentArgs, FluentValue, types::FluentNumber};
31 #[cfg(feature = "langneg")]
32 pub use icu_locale_core::{LanguageIdentifier, langid};
33
34 #[cfg(feature = "langneg")]
35 pub fn negotiate_languages<'a, A>(accept_language: &str, available: &'a [A]) -> A
36 where
37 A: 'a + AsRef<LanguageIdentifier> + PartialEq + Default + Copy,
38 {
39 let mut requested: Vec<(LanguageIdentifier, u16)> = accept_language
41 .split(',')
42 .filter_map(|entry| {
43 let entry = entry.trim();
44 if entry.is_empty() {
45 return None;
46 }
47 let (tag, quality) = if let Some((tag, params)) = entry.split_once(';') {
48 let q = params
49 .trim()
50 .strip_prefix("q=")
51 .and_then(|v| v.parse::<f32>().ok())
52 .unwrap_or(1.0);
53 (tag.trim(), (q * 1000.0) as u16)
54 } else {
55 (entry, 1000)
56 };
57 tag.parse::<LanguageIdentifier>()
58 .ok()
59 .map(|lid| (lid, quality))
60 })
61 .collect();
62 requested.sort_by_key(|entry| std::cmp::Reverse(entry.1));
63
64 for (req, _) in &requested {
66 for avail in available {
67 if avail.as_ref().language == req.language {
68 return *avail;
69 }
70 }
71 }
72 A::default()
73 }
74}