Skip to main content

sofie_mail/
language.rs

1use sofie_core::Show;
2
3use crate::Mailer;
4
5#[cfg(feature = "i18n")]
6pub(crate) use spoken::{Language, Spoken, rendered};
7
8#[cfg(feature = "i18n")]
9pub use spoken::translations;
10
11#[cfg(not(feature = "i18n"))]
12pub(crate) use plain::{Language, rendered};
13
14#[cfg(feature = "i18n")]
15mod spoken {
16    use std::sync::Arc;
17
18    use sofie_core::Show;
19    use sofie_db::Db;
20    use sofie_engine::rendering::Rendering;
21    use sofie_engine::{Template, TemplateError, Translations};
22
23    pub(crate) type Language<'a> = sofie_engine::Language<'a>;
24
25    pub(crate) struct Spoken(pub(crate) Arc<Translations>);
26
27    pub fn translations(db: &Db, translations: Arc<Translations>) {
28        sofie_db::extend(db, Arc::new(Spoken(translations)));
29    }
30
31    pub(crate) fn rendered(
32        template: &Template,
33        data: &(impl Show + ?Sized),
34        language: Language<'_>,
35    ) -> Result<String, TemplateError> {
36        Rendering::new(template).language(language).render(data)
37    }
38}
39
40#[cfg(not(feature = "i18n"))]
41mod plain {
42    use std::marker::PhantomData;
43
44    use sofie_core::Show;
45    use sofie_engine::{Template, TemplateError};
46
47    #[derive(Clone, Copy)]
48    pub(crate) struct Language<'a>(PhantomData<&'a ()>);
49
50    impl Language<'_> {
51        pub(crate) fn plain() -> Self {
52            Self(PhantomData)
53        }
54    }
55
56    pub(crate) fn rendered(
57        template: &Template,
58        data: &(impl Show + ?Sized),
59        _language: Language<'_>,
60    ) -> Result<String, TemplateError> {
61        template.render(data)
62    }
63}
64
65impl Mailer {
66    /// `None`, or a language the app does not declare, writes its source language.
67    #[cfg(feature = "i18n")]
68    #[must_use]
69    pub fn in_language(mut self, locale: Option<sofie_values::Locale>) -> Self {
70        self.locale = locale;
71        self
72    }
73
74    /// `fallback`, in the source language, where no language of the app has the key.
75    #[must_use]
76    pub fn text(&self, key: &str, arguments: &[(&str, String)], fallback: &str) -> String {
77        #[cfg(feature = "i18n")]
78        return self.language().field_error(key, arguments, fallback);
79        #[cfg(not(feature = "i18n"))]
80        {
81            let _ = (key, arguments);
82            fallback.to_owned()
83        }
84    }
85
86    #[cfg(feature = "i18n")]
87    pub(crate) fn language(&self) -> Language<'_> {
88        match &self.translations {
89            Some(translations) => translations.negotiate(self.locale.as_ref(), None),
90            None => Language::plain(),
91        }
92    }
93
94    #[cfg(not(feature = "i18n"))]
95    #[expect(
96        clippy::unused_self,
97        reason = "under the `i18n` feature the mailer holds the translations"
98    )]
99    pub(crate) fn language(&self) -> Language<'_> {
100        Language::plain()
101    }
102
103    pub(crate) fn render(
104        &self,
105        template: &crate::Template,
106        data: &(impl Show + ?Sized),
107    ) -> sofie_core::Result<crate::Body> {
108        template.render(data, self.language())
109    }
110}