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