1use crate::config::{GetLang, LIB_CFG, Mode};
7use crate::error::LibCfgError;
8#[cfg(feature = "lang-detection")]
9use lingua;
10#[cfg(feature = "lang-detection")]
11use lingua::IsoCode639_1;
12use parking_lot::RwLock;
13use std::borrow::Cow;
14use std::collections::BTreeMap;
15use std::env;
16#[cfg(feature = "lang-detection")]
17use std::str::FromStr;
18#[cfg(target_family = "windows")]
19use windows_sys::Win32::Globalization::GetUserDefaultLocaleName;
20#[cfg(target_family = "windows")]
21use windows_sys::Win32::System::SystemServices::LOCALE_NAME_MAX_LENGTH;
22
23pub const ENV_VAR_TPNOTE_SCHEME: &str = "TPNOTE_SCHEME";
26
27pub const ENV_VAR_TPNOTE_EXTENSION_DEFAULT: &str = "TPNOTE_EXTENSION_DEFAULT";
30
31pub const ENV_VAR_TPNOTE_LANG: &str = "TPNOTE_LANG";
36
37pub const ENV_VAR_TPNOTE_LANG_PLUS_ALL: &str = "+all";
40
41pub const ENV_VAR_TPNOTE_LANG_DETECTION: &str = "TPNOTE_LANG_DETECTION";
45
46pub const ENV_VAR_TPNOTE_USER: &str = "TPNOTE_USER";
50
51const ENV_VAR_LOGNAME: &str = "LOGNAME";
53
54const ENV_VAR_USERNAME: &str = "USERNAME";
56
57const ENV_VAR_USER: &str = "USER";
59
60#[cfg(not(target_family = "windows"))]
62const ENV_VAR_LANG: &str = "LANG";
63
64#[derive(Debug)]
67#[allow(dead_code)]
68pub(crate) struct Settings {
69    pub current_scheme: usize,
71    pub author: String,
73    pub lang: String,
78    pub force_lang: String,
82    pub extension_default: String,
84    pub get_lang_filter: GetLang,
86    pub map_lang_filter_btmap: Option<BTreeMap<String, String>>,
90}
91
92const DEFAULT_SETTINGS: Settings = Settings {
93    current_scheme: 0,
94    author: String::new(),
95    lang: String::new(),
96    force_lang: String::new(),
97    extension_default: String::new(),
98    get_lang_filter: GetLang {
99        mode: Mode::Disabled,
100        language_candidates: vec![],
101        relative_distance_min: 0.0,
102        consecutive_words_min: 0,
103        words_total_percentage_min: 0,
104    },
105    map_lang_filter_btmap: None,
106};
107
108impl Default for Settings {
109    #[cfg(not(any(test, doc)))]
110    fn default() -> Self {
112        DEFAULT_SETTINGS
113    }
114
115    #[cfg(any(test, doc))]
116    fn default() -> Self {
119        let mut settings = DEFAULT_SETTINGS;
120        settings.author = String::from("testuser");
121        settings.lang = String::from("ab-AB");
122        settings.extension_default = String::from("md");
123        settings
124    }
125}
126
127#[cfg(not(test))]
129pub(crate) static SETTINGS: RwLock<Settings> = RwLock::new(DEFAULT_SETTINGS);
130
131#[cfg(test)]
132pub(crate) static SETTINGS: RwLock<Settings> = RwLock::new(DEFAULT_SETTINGS);
134
135pub fn set_test_default_settings() -> Result<(), LibCfgError> {
139    let mut settings = SETTINGS.write();
140    settings.update(SchemeSource::Force("default"), None)
141}
142
143#[derive(Debug, Clone)]
145pub(crate) enum SchemeSource<'a> {
146    Force(&'a str),
148    SchemeSyncDefault,
150    SchemeNewDefault(&'a str),
152}
153
154impl Settings {
155    pub(crate) fn update(
174        &mut self,
175        scheme_source: SchemeSource,
176        force_lang: Option<&str>,
179    ) -> Result<(), LibCfgError> {
180        self.update_current_scheme(scheme_source)?;
181        self.update_author();
182        self.update_extension_default();
183        self.update_lang(force_lang);
184        self.update_get_lang_filter();
185        self.update_map_lang_filter_btmap();
186        self.update_env_lang_detection();
187
188        log::trace!(
189            "`SETTINGS` updated (reading config + env. vars.):\n{:#?}",
190            self
191        );
192
193        if let Mode::Error(e) = &self.get_lang_filter.mode {
194            Err(e.clone())
195        } else {
196            Ok(())
197        }
198    }
199
200    pub(crate) fn update_current_scheme(
210        &mut self,
211        scheme_source: SchemeSource,
212    ) -> Result<(), LibCfgError> {
213        let lib_cfg = LIB_CFG.read_recursive();
214
215        let scheme = match scheme_source {
216            SchemeSource::Force(s) => Cow::Borrowed(s),
217            SchemeSource::SchemeSyncDefault => Cow::Borrowed(&*lib_cfg.scheme_sync_default),
218            SchemeSource::SchemeNewDefault(s) => match env::var(ENV_VAR_TPNOTE_SCHEME) {
219                Ok(ed_env) if !ed_env.is_empty() => Cow::Owned(ed_env),
220                Err(_) | Ok(_) => Cow::Borrowed(s),
221            },
222        };
223        self.current_scheme = lib_cfg.scheme_idx(scheme.as_ref())?;
224        Ok(())
225    }
226
227    fn update_author(&mut self) {
230        let author = env::var(ENV_VAR_TPNOTE_USER).unwrap_or_else(|_| {
231            env::var(ENV_VAR_LOGNAME).unwrap_or_else(|_| {
232                env::var(ENV_VAR_USERNAME)
233                    .unwrap_or_else(|_| env::var(ENV_VAR_USER).unwrap_or_default())
234            })
235        });
236
237        self.author = author;
239    }
240
241    fn update_extension_default(&mut self) {
245        let ext = match env::var(ENV_VAR_TPNOTE_EXTENSION_DEFAULT) {
247            Ok(ed_env) if !ed_env.is_empty() => ed_env,
248            Err(_) | Ok(_) => {
249                let lib_cfg = LIB_CFG.read_recursive();
250                lib_cfg.scheme[self.current_scheme]
251                    .filename
252                    .extension_default
253                    .to_string()
254            }
255        };
256        self.extension_default = ext;
257    }
258
259    fn update_lang(&mut self, force_lang: Option<&str>) {
266        let mut lang = String::new();
269        let tpnotelang = env::var(ENV_VAR_TPNOTE_LANG).ok();
271        #[cfg(not(target_family = "windows"))]
273        if let Some(tpnotelang) = tpnotelang {
274            lang = tpnotelang;
275        } else {
276            if let Ok(lang_env) = env::var(ENV_VAR_LANG) {
279                if !lang_env.is_empty() {
280                    let mut language = "";
282                    let mut territory = "";
284                    if let Some((l, lang_env)) = lang_env.split_once('_') {
285                        language = l;
286                        if let Some((t, _codeset)) = lang_env.split_once('.') {
287                            territory = t;
288                        }
289                    }
290                    lang = language.to_string();
291                    lang.push('-');
292                    lang.push_str(territory);
293                }
294            }
295        }
296
297        #[cfg(target_family = "windows")]
300        if let Some(tpnotelang) = tpnotelang {
301            lang = tpnotelang;
302        } else {
303            let mut buf = [0u16; LOCALE_NAME_MAX_LENGTH as usize];
304            let len = unsafe { GetUserDefaultLocaleName(buf.as_mut_ptr(), buf.len() as i32) };
305            if len > 0 {
306                lang = String::from_utf16_lossy(&buf[..((len - 1) as usize)]);
307            }
308        };
309
310        self.lang = lang;
312
313        self.force_lang = match force_lang {
315            Some("") => self.lang.clone(),
316            Some(lang) => lang.to_owned(),
317            None => String::new(),
318        };
319    }
320
321    #[cfg(feature = "lang-detection")]
328    fn update_get_lang_filter(&mut self) {
329        use crate::config::Mode;
330
331        {
332            let lib_cfg = LIB_CFG.read_recursive();
333            let current_scheme = &lib_cfg.scheme[self.current_scheme];
334
335            self.get_lang_filter = current_scheme.tmpl.filter.get_lang.clone();
337        } if matches!(self.get_lang_filter.mode, Mode::Disabled) {
341            return;
342        }
343
344        let iso_codes = &mut self.get_lang_filter.language_candidates;
346
347        if iso_codes.is_empty() {
349            return;
350        }
351
352        if !self.lang.is_empty() {
355            if let Some((lang_subtag, _)) = self.lang.split_once('-') {
356                if let Ok(iso_code) = IsoCode639_1::from_str(lang_subtag) {
357                    if !iso_codes.contains(&iso_code) {
358                        iso_codes.push(iso_code);
359                    }
360                }
361            }
362        }
363
364        if iso_codes.len() <= 1 {
366            self.get_lang_filter.mode = Mode::Error(LibCfgError::NotEnoughLanguageCodes {
367                language_code: iso_codes[0].to_string(),
368            })
369        }
370    }
371
372    #[cfg(not(feature = "lang-detection"))]
373    fn update_get_lang_filter(&mut self) {
375        self.get_lang_filter.mode = Mode::Disabled;
376    }
377
378    fn update_map_lang_filter_btmap(&mut self) {
382        let mut btm = BTreeMap::new();
383        let lib_cfg = LIB_CFG.read_recursive();
384        for l in &lib_cfg.scheme[self.current_scheme].tmpl.filter.map_lang {
385            if l.len() >= 2 {
386                btm.insert(l[0].to_string(), l[1].to_string());
387            };
388        }
389        if !self.lang.is_empty() {
391            if let Some((lang_subtag, _)) = self.lang.split_once('-') {
392                if !lang_subtag.is_empty() && !btm.contains_key(lang_subtag) {
394                    btm.insert(lang_subtag.to_string(), self.lang.to_string());
395                }
396            };
397        }
398
399        self.map_lang_filter_btmap = Some(btm);
401    }
402
403    #[cfg(feature = "lang-detection")]
409    fn update_env_lang_detection(&mut self) {
410        use crate::config::Mode;
411
412        if let Ok(env_var) = env::var(ENV_VAR_TPNOTE_LANG_DETECTION) {
413            if env_var.is_empty() {
414                self.get_lang_filter.mode = Mode::Disabled;
416                self.map_lang_filter_btmap = None;
417                log::debug!(
418                    "Empty env. var. `{}` disables the `lang-detection` feature.",
419                    ENV_VAR_TPNOTE_LANG_DETECTION
420                );
421                return;
422            }
423
424            let mut hm: BTreeMap<String, String> = BTreeMap::new();
426            let mut all_languages_selected = false;
427            let iso_codes = env_var
428                .split(',')
429                .map(|t| {
430                    let t = t.trim();
431                    if let Some((lang_subtag, _)) = t.split_once('-') {
432                        if !lang_subtag.is_empty() && !hm.contains_key(lang_subtag) {
434                            hm.insert(lang_subtag.to_string(), t.to_string());
435                        };
436                        lang_subtag
437                    } else {
438                        t
439                    }
440                })
441                .filter(|&l| {
443                    if l == ENV_VAR_TPNOTE_LANG_PLUS_ALL {
444                        all_languages_selected = true;
445                        false
447                    } else {
448                        true
450                    }
451                })
452                .map(|l| {
453                    IsoCode639_1::from_str(l.trim()).map_err(|_| {
454                        let mut all_langs = lingua::Language::all()
457                            .iter()
458                            .map(|l| {
459                                let mut s = l.iso_code_639_1().to_string();
460                                s.push_str(", ");
461                                s
462                            })
463                            .collect::<Vec<String>>();
464                        all_langs.sort();
465                        let mut all_langs = all_langs.into_iter().collect::<String>();
466                        all_langs.truncate(all_langs.len() - ", ".len());
467                        LibCfgError::ParseLanguageCode {
469                            language_code: l.into(),
470                            all_langs,
471                        }
472                    })
473                })
474                .collect::<Result<Vec<IsoCode639_1>, LibCfgError>>();
475
476            match iso_codes {
477                Ok(mut iso_codes) => {
479                    if !self.lang.is_empty() {
482                        if let Some(lang_subtag) = self.lang.split('-').next() {
483                            if let Ok(iso_code) = IsoCode639_1::from_str(lang_subtag) {
484                                if !iso_codes.contains(&iso_code) {
485                                    iso_codes.push(iso_code);
486                                }
487                                if lang_subtag != self.lang && !hm.contains_key(lang_subtag) {
489                                    hm.insert(lang_subtag.to_string(), self.lang.to_string());
490                                }
491                            }
492                        }
493                    }
494
495                    if all_languages_selected {
497                        self.get_lang_filter.language_candidates = vec![];
498                        if matches!(self.get_lang_filter.mode, Mode::Disabled) {
499                            self.get_lang_filter.mode = Mode::Multilingual;
500                        }
501                    } else {
502                        match iso_codes.len() {
503                            0 => self.get_lang_filter.mode = Mode::Disabled,
504                            1 => {
505                                self.get_lang_filter.mode =
506                                    Mode::Error(LibCfgError::NotEnoughLanguageCodes {
507                                        language_code: iso_codes[0].to_string(),
508                                    })
509                            }
510                            _ => {
511                                self.get_lang_filter.language_candidates = iso_codes;
512                                if matches!(self.get_lang_filter.mode, Mode::Disabled) {
513                                    self.get_lang_filter.mode = Mode::Multilingual;
514                                }
515                            }
516                        }
517                    }
518                    self.map_lang_filter_btmap = Some(hm);
519                }
520                Err(e) =>
522                {
524                    self.get_lang_filter.mode = Mode::Error(e);
525                }
526            }
527        }
528    }
529
530    #[cfg(not(feature = "lang-detection"))]
532    fn update_env_lang_detection(&mut self) {
533        if let Ok(env_var) = env::var(ENV_VAR_TPNOTE_LANG_DETECTION) {
534            if !env_var.is_empty() {
535                self.get_lang_filter.mode = Mode::Disabled;
536                self.map_lang_filter_btmap = None;
537                log::debug!(
538                    "Ignoring the env. var. `{}`. The `lang-detection` feature \
539                 is not included in this build.",
540                    ENV_VAR_TPNOTE_LANG_DETECTION
541                );
542            }
543        }
544    }
545}
546#[cfg(test)]
547mod tests {
548    use super::*;
549    #[test]
553    fn test_update_author_setting() {
554        let mut settings = Settings::default();
555        unsafe {
556            env::set_var(ENV_VAR_LOGNAME, "testauthor");
557        }
558        settings.update_author();
559        assert_eq!(settings.author, "testauthor");
560    }
561
562    #[test]
563    fn test_update_extension_default_setting() {
564        let mut settings = Settings::default();
565        unsafe {
566            env::set_var(ENV_VAR_TPNOTE_EXTENSION_DEFAULT, "markdown");
567        }
568        settings.update_extension_default();
569        assert_eq!(settings.extension_default, "markdown");
570
571        let mut settings = Settings::default();
572        unsafe {
573            std::env::remove_var(ENV_VAR_TPNOTE_EXTENSION_DEFAULT);
574        }
575        settings.update_extension_default();
576        assert_eq!(settings.extension_default, "md");
577    }
578
579    #[test]
580    #[cfg(not(target_family = "windows"))]
581    fn test_update_lang_setting() {
582        let mut settings = Settings::default();
584        unsafe {
585            env::remove_var(ENV_VAR_TPNOTE_LANG);
586            env::set_var(ENV_VAR_LANG, "en_GB.UTF-8");
587        }
588        settings.update_lang(None);
589        assert_eq!(settings.lang, "en-GB");
590
591        let mut settings = Settings::default();
593        unsafe {
594            env::remove_var(ENV_VAR_TPNOTE_LANG);
595            env::set_var(ENV_VAR_LANG, "");
596        }
597        settings.update_lang(None);
598        assert_eq!(settings.lang, "");
599
600        let mut settings = Settings::default();
602        unsafe {
603            env::set_var(ENV_VAR_TPNOTE_LANG, "it-IT");
604            env::set_var(ENV_VAR_LANG, "en_GB.UTF-8");
605        }
606        settings.update_lang(None);
607        assert_eq!(settings.lang, "it-IT");
608    }
609
610    #[test]
611    #[cfg(feature = "lang-detection")]
612    fn test_update_get_lang_filter_setting() {
613        let mut settings = Settings {
615            lang: "en-GB".to_string(),
616            ..Default::default()
617        };
618        settings.update_get_lang_filter();
619
620        let output_get_lang_filter = settings
621            .get_lang_filter
622            .language_candidates
623            .iter()
624            .map(|l| {
625                let mut l = l.to_string();
626                l.push(' ');
627                l
628            })
629            .collect::<String>();
630        assert_eq!(output_get_lang_filter, "en fr de ");
631
632        let mut settings = Settings {
635            lang: "it-IT".to_string(),
636            ..Default::default()
637        };
638        settings.update_get_lang_filter();
639
640        let output_get_lang_filter = settings
641            .get_lang_filter
642            .language_candidates
643            .iter()
644            .map(|l| {
645                let mut l = l.to_string();
646                l.push(' ');
647                l
648            })
649            .collect::<String>();
650        assert_eq!(output_get_lang_filter, "en fr de it ");
651    }
652
653    #[test]
654    fn test_update_map_lang_filter_hmap_setting() {
655        let mut settings = Settings {
657            lang: "it-IT".to_string(),
658            ..Default::default()
659        };
660        settings.update_map_lang_filter_btmap();
661
662        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
663
664        assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
665        assert_eq!(output_map_lang_filter.get("et").unwrap(), "et-ET");
666        assert_eq!(output_map_lang_filter.get("it").unwrap(), "it-IT");
667
668        let mut settings = Settings {
671            lang: "it".to_string(),
672            ..Default::default()
673        };
674        settings.update_map_lang_filter_btmap();
675
676        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
677
678        assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
679        assert_eq!(output_map_lang_filter.get("et").unwrap(), "et-ET");
680        assert_eq!(output_map_lang_filter.get("it"), None);
681    }
682
683    #[test]
684    #[cfg(feature = "lang-detection")]
685    fn test_update_env_lang_detection() {
686        let mut settings = Settings {
689            lang: "en-GB".to_string(),
690            ..Default::default()
691        };
692        unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "fr-FR, de-DE, hu") };
693        settings.update_env_lang_detection();
694
695        let output_get_lang_filter = settings
696            .get_lang_filter
697            .language_candidates
698            .iter()
699            .map(|l| {
700                let mut l = l.to_string();
701                l.push(' ');
702                l
703            })
704            .collect::<String>();
705        assert_eq!(output_get_lang_filter, "fr de hu en ");
706
707        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
708        assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
709        assert_eq!(output_map_lang_filter.get("fr").unwrap(), "fr-FR");
710        assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
711
712        let mut settings = Settings {
715            lang: "en-GB".to_string(),
716            ..Default::default()
717        };
718        unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, de-AT, en-US") };
719        settings.update_env_lang_detection();
720
721        let output_get_lang_filter = settings
722            .get_lang_filter
723            .language_candidates
724            .iter()
725            .map(|l| {
726                let mut l = l.to_string();
727                l.push(' ');
728                l
729            })
730            .collect::<String>();
731        assert_eq!(output_get_lang_filter, "de de en ");
732
733        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
734        assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
735        assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-US");
736
737        let mut settings = Settings {
740            lang: "en-GB".to_string(),
741            ..Default::default()
742        };
743        unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, +all, en-US") };
744        settings.update_env_lang_detection();
745
746        assert!(settings.get_lang_filter.language_candidates.is_empty());
747
748        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
749        assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
750        assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-US");
751
752        let mut settings = Settings {
755            lang: "en-GB".to_string(),
756            ..Default::default()
757        };
758        unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, de-AT, en") };
759        settings.update_env_lang_detection();
760
761        let output_get_lang_filter = settings
762            .get_lang_filter
763            .language_candidates
764            .iter()
765            .map(|l| {
766                let mut l = l.to_string();
767                l.push(' ');
768                l
769            })
770            .collect::<String>();
771        assert_eq!(output_get_lang_filter, "de de en ");
772
773        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
774        assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
775        assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
776
777        let mut settings = Settings {
780            lang: "en-GB".to_string(),
781            ..Default::default()
782        };
783        unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, +all, de-AT, en") };
784        settings.update_env_lang_detection();
785
786        assert!(settings.get_lang_filter.language_candidates.is_empty());
787
788        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
789        assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
790        assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
791
792        let mut settings = Settings {
794            lang: "en-GB".to_string(),
795            ..Default::default()
796        };
797        unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "fr-FR, de-DE, hu") };
798        settings.update_env_lang_detection();
799
800        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
801        assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
802        assert_eq!(output_map_lang_filter.get("fr").unwrap(), "fr-FR");
803        assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
804
805        let mut settings = Settings {
808            lang: "".to_string(),
809            ..Default::default()
810        };
811        unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "") };
812        settings.update_env_lang_detection();
813
814        assert_eq!(settings.get_lang_filter.mode, Mode::Disabled);
815        assert!(settings.map_lang_filter_btmap.is_none());
816
817        let mut settings = Settings {
820            lang: "xy-XY".to_string(),
821            ..Default::default()
822        };
823        unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "en-GB, fr") };
824        settings.update_env_lang_detection();
825
826        let output_get_lang_filter = settings
827            .get_lang_filter
828            .language_candidates
829            .iter()
830            .map(|l| {
831                let mut l = l.to_string();
832                l.push(' ');
833                l
834            })
835            .collect::<String>();
836        assert_eq!(output_get_lang_filter, "en fr ");
837
838        let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
839        assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
840
841        let mut settings = Settings {
844            lang: "en-GB".to_string(),
845            ..Default::default()
846        };
847        unsafe {
848            env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, xy-XY");
849        }
850        settings.update_env_lang_detection();
851
852        assert!(matches!(settings.get_lang_filter.mode, Mode::Error(..)));
853        assert!(settings.map_lang_filter_btmap.is_none());
854        let mut settings = Settings {
857            lang: "en-GB".to_string(),
858            ..Default::default()
859        };
860        unsafe {
861            env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "");
862        }
863        settings.update_env_lang_detection();
864
865        assert!(matches!(settings.get_lang_filter.mode, Mode::Disabled));
866        assert!(settings.map_lang_filter_btmap.is_none());
867    }
868}