Skip to main content

fluent_typed/
lib.rs

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/// Internal build-pipeline pieces exposed for the benchmark suite only. Gated
21/// behind the non-default `bench-internals` feature; not a stable API.
22#[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        // Parse Accept-Language header into (LanguageIdentifier, quality) pairs, sorted by quality descending
40        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        // Find the first available language whose language subtag matches a requested one
65        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}