Skip to main content

fluent_typed/
lib.rs

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