1use crate::config::{GetLang, Mode, LIB_CFG};
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,
76 pub extension_default: String,
78 pub get_lang_filter: GetLang,
80 pub map_lang_filter_btmap: Option<BTreeMap<String, String>>,
84}
85
86const DEFAULT_SETTINGS: Settings = Settings {
87 current_scheme: 0,
88 author: String::new(),
89 lang: String::new(),
90 extension_default: String::new(),
91 get_lang_filter: GetLang {
92 mode: Mode::Disabled,
93 language_candidates: vec![],
94 relative_distance_min: 0.0,
95 consecutive_words_min: 0,
96 words_total_percentage_min: 0,
97 },
98 map_lang_filter_btmap: None,
99};
100
101impl Default for Settings {
102 #[cfg(not(any(test, doc)))]
103 fn default() -> Self {
105 DEFAULT_SETTINGS
106 }
107
108 #[cfg(any(test, doc))]
109 fn default() -> Self {
112 let mut settings = DEFAULT_SETTINGS;
113 settings.author = String::from("testuser");
114 settings.lang = String::from("ab-AB");
115 settings.extension_default = String::from("md");
116 settings
117 }
118}
119
120#[cfg(not(test))]
122pub(crate) static SETTINGS: RwLock<Settings> = RwLock::new(DEFAULT_SETTINGS);
123
124#[cfg(test)]
125pub(crate) static SETTINGS: RwLock<Settings> = RwLock::new(DEFAULT_SETTINGS);
127
128pub fn set_test_default_settings() -> Result<(), LibCfgError> {
132 let mut settings = SETTINGS.write();
133 settings.update(SchemeSource::Force("default"), None)
134}
135
136#[derive(Debug, Clone)]
138pub(crate) enum SchemeSource<'a> {
139 Force(&'a str),
141 SchemeSyncDefault,
143 SchemeNewDefault(&'a str),
145}
146
147impl Settings {
148 pub(crate) fn update(
167 &mut self,
168 scheme_source: SchemeSource,
169 force_lang: Option<&str>,
170 ) -> Result<(), LibCfgError> {
171 self.update_current_scheme(scheme_source)?;
172 self.update_author();
173 self.update_extension_default();
174 self.update_lang(force_lang);
175 self.update_get_lang_filter(force_lang.is_some());
176 self.update_map_lang_filter_btmap();
177 self.update_env_lang_detection(force_lang.is_some());
178
179 log::trace!(
180 "`SETTINGS` updated (reading config + env. vars.):\n{:#?}",
181 self
182 );
183
184 if let Mode::Error(e) = &self.get_lang_filter.mode {
185 Err(e.clone())
186 } else {
187 Ok(())
188 }
189 }
190
191 pub(crate) fn update_current_scheme(
201 &mut self,
202 scheme_source: SchemeSource,
203 ) -> Result<(), LibCfgError> {
204 let lib_cfg = LIB_CFG.read_recursive();
205
206 let scheme = match scheme_source {
207 SchemeSource::Force(s) => Cow::Borrowed(s),
208 SchemeSource::SchemeSyncDefault => Cow::Borrowed(&*lib_cfg.scheme_sync_default),
209 SchemeSource::SchemeNewDefault(s) => match env::var(ENV_VAR_TPNOTE_SCHEME) {
210 Ok(ed_env) if !ed_env.is_empty() => Cow::Owned(ed_env),
211 Err(_) | Ok(_) => Cow::Borrowed(s),
212 },
213 };
214 self.current_scheme = lib_cfg.scheme_idx(scheme.as_ref())?;
215 Ok(())
216 }
217
218 fn update_author(&mut self) {
221 let author = env::var(ENV_VAR_TPNOTE_USER).unwrap_or_else(|_| {
222 env::var(ENV_VAR_LOGNAME).unwrap_or_else(|_| {
223 env::var(ENV_VAR_USERNAME)
224 .unwrap_or_else(|_| env::var(ENV_VAR_USER).unwrap_or_default())
225 })
226 });
227
228 self.author = author;
230 }
231
232 fn update_extension_default(&mut self) {
236 let ext = match env::var(ENV_VAR_TPNOTE_EXTENSION_DEFAULT) {
238 Ok(ed_env) if !ed_env.is_empty() => ed_env,
239 Err(_) | Ok(_) => {
240 let lib_cfg = LIB_CFG.read_recursive();
241 lib_cfg.scheme[self.current_scheme]
242 .filename
243 .extension_default
244 .to_string()
245 }
246 };
247 self.extension_default = ext;
248 }
249
250 fn update_lang(&mut self, force_lang: Option<&str>) {
254 if let Some(l) = force_lang {
256 if !l.is_empty() {
257 self.lang = l.to_string();
258 return;
259 }
260 }
261
262 let mut lang = String::new();
265 let tpnotelang = env::var(ENV_VAR_TPNOTE_LANG).ok();
267 #[cfg(not(target_family = "windows"))]
269 if let Some(tpnotelang) = tpnotelang {
270 lang = tpnotelang;
271 } else {
272 if let Ok(lang_env) = env::var(ENV_VAR_LANG) {
275 if !lang_env.is_empty() {
276 let mut language = "";
278 let mut territory = "";
280 if let Some((l, lang_env)) = lang_env.split_once('_') {
281 language = l;
282 if let Some((t, _codeset)) = lang_env.split_once('.') {
283 territory = t;
284 }
285 }
286 lang = language.to_string();
287 lang.push('-');
288 lang.push_str(territory);
289 }
290 }
291 }
292
293 #[cfg(target_family = "windows")]
296 if let Some(tpnotelang) = tpnotelang {
297 lang = tpnotelang;
298 } else {
299 let mut buf = [0u16; LOCALE_NAME_MAX_LENGTH as usize];
300 let len = unsafe { GetUserDefaultLocaleName(buf.as_mut_ptr(), buf.len() as i32) };
301 if len > 0 {
302 lang = String::from_utf16_lossy(&buf[..((len - 1) as usize)]);
303 }
304 };
305
306 self.lang = lang;
308 }
309
310 #[cfg(feature = "lang-detection")]
319 fn update_get_lang_filter(&mut self, force_lang: bool) {
320 use crate::config::Mode;
321
322 {
323 let lib_cfg = LIB_CFG.read_recursive();
324 let current_scheme = &lib_cfg.scheme[self.current_scheme];
325
326 self.get_lang_filter = current_scheme.tmpl.filter.get_lang.clone();
328 } if force_lang {
332 self.get_lang_filter.mode = Mode::Disabled;
333 return;
334 }
335
336 if matches!(self.get_lang_filter.mode, Mode::Disabled) {
338 return;
339 }
340
341 let iso_codes = &mut self.get_lang_filter.language_candidates;
343
344 if iso_codes.is_empty() {
346 return;
347 }
348
349 if !self.lang.is_empty() {
352 if let Some((lang_subtag, _)) = self.lang.split_once('-') {
353 if let Ok(iso_code) = IsoCode639_1::from_str(lang_subtag) {
354 if !iso_codes.contains(&iso_code) {
355 iso_codes.push(iso_code);
356 }
357 }
358 }
359 }
360
361 if iso_codes.len() <= 1 {
363 self.get_lang_filter.mode = Mode::Error(LibCfgError::NotEnoughLanguageCodes {
364 language_code: iso_codes[0].to_string(),
365 })
366 }
367 }
368
369 #[cfg(not(feature = "lang-detection"))]
370 fn update_get_lang_filter(&mut self, _force_lang: bool) {
372 self.get_lang_filter.mode = Mode::Disabled;
373 }
374
375 fn update_map_lang_filter_btmap(&mut self) {
379 let mut btm = BTreeMap::new();
380 let lib_cfg = LIB_CFG.read_recursive();
381 for l in &lib_cfg.scheme[self.current_scheme].tmpl.filter.map_lang {
382 if l.len() >= 2 {
383 btm.insert(l[0].to_string(), l[1].to_string());
384 };
385 }
386 if !self.lang.is_empty() {
388 if let Some((lang_subtag, _)) = self.lang.split_once('-') {
389 if !lang_subtag.is_empty() && !btm.contains_key(lang_subtag) {
391 btm.insert(lang_subtag.to_string(), self.lang.to_string());
392 }
393 };
394 }
395
396 self.map_lang_filter_btmap = Some(btm);
398 }
399
400 #[cfg(feature = "lang-detection")]
406 fn update_env_lang_detection(&mut self, force_lang: bool) {
407 use crate::config::Mode;
408
409 if let Ok(env_var) = env::var(ENV_VAR_TPNOTE_LANG_DETECTION) {
410 if env_var.is_empty() {
411 self.get_lang_filter.mode = Mode::Disabled;
413 self.map_lang_filter_btmap = None;
414 log::debug!(
415 "Empty env. var. `{}` disables the `lang-detection` feature.",
416 ENV_VAR_TPNOTE_LANG_DETECTION
417 );
418 return;
419 }
420
421 let mut hm: BTreeMap<String, String> = BTreeMap::new();
423 let mut all_languages_selected = false;
424 let iso_codes = env_var
425 .split(',')
426 .map(|t| {
427 let t = t.trim();
428 if let Some((lang_subtag, _)) = t.split_once('-') {
429 if !lang_subtag.is_empty() && !hm.contains_key(lang_subtag) {
431 hm.insert(lang_subtag.to_string(), t.to_string());
432 };
433 lang_subtag
434 } else {
435 t
436 }
437 })
438 .filter(|&l| {
440 if l == ENV_VAR_TPNOTE_LANG_PLUS_ALL {
441 all_languages_selected = true;
442 false
444 } else {
445 true
447 }
448 })
449 .map(|l| {
450 IsoCode639_1::from_str(l.trim()).map_err(|_| {
451 let mut all_langs = lingua::Language::all()
454 .iter()
455 .map(|l| {
456 let mut s = l.iso_code_639_1().to_string();
457 s.push_str(", ");
458 s
459 })
460 .collect::<Vec<String>>();
461 all_langs.sort();
462 let mut all_langs = all_langs.into_iter().collect::<String>();
463 all_langs.truncate(all_langs.len() - ", ".len());
464 LibCfgError::ParseLanguageCode {
466 language_code: l.into(),
467 all_langs,
468 }
469 })
470 })
471 .collect::<Result<Vec<IsoCode639_1>, LibCfgError>>();
472
473 match iso_codes {
474 Ok(mut iso_codes) => {
476 if !self.lang.is_empty() {
479 if let Some(lang_subtag) = self.lang.split('-').next() {
480 if let Ok(iso_code) = IsoCode639_1::from_str(lang_subtag) {
481 if !iso_codes.contains(&iso_code) {
482 iso_codes.push(iso_code);
483 }
484 if lang_subtag != self.lang && !hm.contains_key(lang_subtag) {
486 hm.insert(lang_subtag.to_string(), self.lang.to_string());
487 }
488 }
489 }
490 }
491
492 if all_languages_selected {
494 self.get_lang_filter.language_candidates = vec![];
495 if matches!(self.get_lang_filter.mode, Mode::Disabled) {
496 self.get_lang_filter.mode = Mode::Multilingual;
497 }
498 } else {
499 match iso_codes.len() {
500 0 => self.get_lang_filter.mode = Mode::Disabled,
501 1 => {
502 self.get_lang_filter.mode =
503 Mode::Error(LibCfgError::NotEnoughLanguageCodes {
504 language_code: iso_codes[0].to_string(),
505 })
506 }
507 _ => {
508 self.get_lang_filter.language_candidates = iso_codes;
509 if matches!(self.get_lang_filter.mode, Mode::Disabled) {
510 self.get_lang_filter.mode = Mode::Multilingual;
511 }
512 }
513 }
514 }
515 self.map_lang_filter_btmap = Some(hm);
516 }
517 Err(e) =>
519 {
521 self.get_lang_filter.mode = Mode::Error(e);
522 }
523 }
524
525 if force_lang {
528 self.get_lang_filter.mode = Mode::Disabled;
529 }
530 }
531 }
532
533 #[cfg(not(feature = "lang-detection"))]
535 fn update_env_lang_detection(&mut self, _force_lang: bool) {
536 if let Ok(env_var) = env::var(ENV_VAR_TPNOTE_LANG_DETECTION) {
537 if !env_var.is_empty() {
538 self.get_lang_filter.mode = Mode::Disabled;
539 self.map_lang_filter_btmap = None;
540 log::debug!(
541 "Ignoring the env. var. `{}`. The `lang-detection` feature \
542 is not included in this build.",
543 ENV_VAR_TPNOTE_LANG_DETECTION
544 );
545 }
546 }
547 }
548}
549#[cfg(test)]
550mod tests {
551 use super::*;
552 #[test]
556 fn test_update_author_setting() {
557 let mut settings = Settings::default();
558 unsafe {
559 env::set_var(ENV_VAR_LOGNAME, "testauthor");
560 }
561 settings.update_author();
562 assert_eq!(settings.author, "testauthor");
563 }
564
565 #[test]
566 fn test_update_extension_default_setting() {
567 let mut settings = Settings::default();
568 unsafe {
569 env::set_var(ENV_VAR_TPNOTE_EXTENSION_DEFAULT, "markdown");
570 }
571 settings.update_extension_default();
572 assert_eq!(settings.extension_default, "markdown");
573
574 let mut settings = Settings::default();
575 unsafe {
576 std::env::remove_var(ENV_VAR_TPNOTE_EXTENSION_DEFAULT);
577 }
578 settings.update_extension_default();
579 assert_eq!(settings.extension_default, "md");
580 }
581
582 #[test]
583 #[cfg(not(target_family = "windows"))]
584 fn test_update_lang_setting() {
585 let mut settings = Settings::default();
587 unsafe {
588 env::remove_var(ENV_VAR_TPNOTE_LANG);
589 env::set_var(ENV_VAR_LANG, "en_GB.UTF-8");
590 }
591 settings.update_lang(None);
592 assert_eq!(settings.lang, "en-GB");
593
594 let mut settings = Settings::default();
596 unsafe {
597 env::remove_var(ENV_VAR_TPNOTE_LANG);
598 env::set_var(ENV_VAR_LANG, "");
599 }
600 settings.update_lang(None);
601 assert_eq!(settings.lang, "");
602
603 let mut settings = Settings::default();
605 unsafe {
606 env::set_var(ENV_VAR_TPNOTE_LANG, "it-IT");
607 env::set_var(ENV_VAR_LANG, "en_GB.UTF-8");
608 }
609 settings.update_lang(None);
610 assert_eq!(settings.lang, "it-IT");
611 }
612
613 #[test]
614 #[cfg(feature = "lang-detection")]
615 fn test_update_get_lang_filter_setting() {
616 let mut settings = Settings {
618 lang: "en-GB".to_string(),
619 ..Default::default()
620 };
621 settings.update_get_lang_filter(false);
622
623 let output_get_lang_filter = settings
624 .get_lang_filter
625 .language_candidates
626 .iter()
627 .map(|l| {
628 let mut l = l.to_string();
629 l.push(' ');
630 l
631 })
632 .collect::<String>();
633 assert_eq!(output_get_lang_filter, "en fr de ");
634
635 let mut settings = Settings {
638 lang: "it-IT".to_string(),
639 ..Default::default()
640 };
641 settings.update_get_lang_filter(false);
642
643 let output_get_lang_filter = settings
644 .get_lang_filter
645 .language_candidates
646 .iter()
647 .map(|l| {
648 let mut l = l.to_string();
649 l.push(' ');
650 l
651 })
652 .collect::<String>();
653 assert_eq!(output_get_lang_filter, "en fr de it ");
654 }
655
656 #[test]
657 fn test_update_map_lang_filter_hmap_setting() {
658 let mut settings = Settings {
660 lang: "it-IT".to_string(),
661 ..Default::default()
662 };
663 settings.update_map_lang_filter_btmap();
664
665 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
666
667 assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
668 assert_eq!(output_map_lang_filter.get("et").unwrap(), "et-ET");
669 assert_eq!(output_map_lang_filter.get("it").unwrap(), "it-IT");
670
671 let mut settings = Settings {
674 lang: "it".to_string(),
675 ..Default::default()
676 };
677 settings.update_map_lang_filter_btmap();
678
679 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
680
681 assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
682 assert_eq!(output_map_lang_filter.get("et").unwrap(), "et-ET");
683 assert_eq!(output_map_lang_filter.get("it"), None);
684 }
685
686 #[test]
687 #[cfg(feature = "lang-detection")]
688 fn test_update_env_lang_detection() {
689 let mut settings = Settings {
692 lang: "en-GB".to_string(),
693 ..Default::default()
694 };
695 unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "fr-FR, de-DE, hu") };
696 settings.update_env_lang_detection(false);
697
698 let output_get_lang_filter = settings
699 .get_lang_filter
700 .language_candidates
701 .iter()
702 .map(|l| {
703 let mut l = l.to_string();
704 l.push(' ');
705 l
706 })
707 .collect::<String>();
708 assert_eq!(output_get_lang_filter, "fr de hu en ");
709
710 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
711 assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
712 assert_eq!(output_map_lang_filter.get("fr").unwrap(), "fr-FR");
713 assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
714
715 let mut settings = Settings {
718 lang: "en-GB".to_string(),
719 ..Default::default()
720 };
721 unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, de-AT, en-US") };
722 settings.update_env_lang_detection(false);
723
724 let output_get_lang_filter = settings
725 .get_lang_filter
726 .language_candidates
727 .iter()
728 .map(|l| {
729 let mut l = l.to_string();
730 l.push(' ');
731 l
732 })
733 .collect::<String>();
734 assert_eq!(output_get_lang_filter, "de de en ");
735
736 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
737 assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
738 assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-US");
739
740 let mut settings = Settings {
743 lang: "en-GB".to_string(),
744 ..Default::default()
745 };
746 unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, +all, en-US") };
747 settings.update_env_lang_detection(false);
748
749 assert!(settings.get_lang_filter.language_candidates.is_empty());
750
751 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
752 assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
753 assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-US");
754
755 let mut settings = Settings {
758 lang: "en-GB".to_string(),
759 ..Default::default()
760 };
761 unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, de-AT, en") };
762 settings.update_env_lang_detection(false);
763
764 let output_get_lang_filter = settings
765 .get_lang_filter
766 .language_candidates
767 .iter()
768 .map(|l| {
769 let mut l = l.to_string();
770 l.push(' ');
771 l
772 })
773 .collect::<String>();
774 assert_eq!(output_get_lang_filter, "de de en ");
775
776 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
777 assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
778 assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
779
780 let mut settings = Settings {
783 lang: "en-GB".to_string(),
784 ..Default::default()
785 };
786 unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, +all, de-AT, en") };
787 settings.update_env_lang_detection(false);
788
789 assert!(settings.get_lang_filter.language_candidates.is_empty());
790
791 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
792 assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
793 assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
794
795 let mut settings = Settings {
797 lang: "en-GB".to_string(),
798 ..Default::default()
799 };
800 unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "fr-FR, de-DE, hu") };
801 settings.update_env_lang_detection(true);
802
803 assert_eq!(settings.get_lang_filter.mode, Mode::Disabled);
805
806 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
807 assert_eq!(output_map_lang_filter.get("de").unwrap(), "de-DE");
808 assert_eq!(output_map_lang_filter.get("fr").unwrap(), "fr-FR");
809 assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
810
811 let mut settings = Settings {
814 lang: "".to_string(),
815 ..Default::default()
816 };
817 unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "") };
818 settings.update_env_lang_detection(false);
819
820 assert_eq!(settings.get_lang_filter.mode, Mode::Disabled);
821 assert!(settings.map_lang_filter_btmap.is_none());
822
823 let mut settings = Settings {
826 lang: "xy-XY".to_string(),
827 ..Default::default()
828 };
829 unsafe { env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "en-GB, fr") };
830 settings.update_env_lang_detection(false);
831
832 let output_get_lang_filter = settings
833 .get_lang_filter
834 .language_candidates
835 .iter()
836 .map(|l| {
837 let mut l = l.to_string();
838 l.push(' ');
839 l
840 })
841 .collect::<String>();
842 assert_eq!(output_get_lang_filter, "en fr ");
843
844 let output_map_lang_filter = settings.map_lang_filter_btmap.unwrap();
845 assert_eq!(output_map_lang_filter.get("en").unwrap(), "en-GB");
846
847 let mut settings = Settings {
850 lang: "en-GB".to_string(),
851 ..Default::default()
852 };
853 unsafe {
854 env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "de-DE, xy-XY");
855 }
856 settings.update_env_lang_detection(false);
857
858 assert!(matches!(settings.get_lang_filter.mode, Mode::Error(..)));
859 assert!(settings.map_lang_filter_btmap.is_none());
860 let mut settings = Settings {
863 lang: "en-GB".to_string(),
864 ..Default::default()
865 };
866 unsafe {
867 env::set_var(ENV_VAR_TPNOTE_LANG_DETECTION, "");
868 }
869 settings.update_env_lang_detection(false);
870
871 assert!(matches!(settings.get_lang_filter.mode, Mode::Disabled));
872 assert!(settings.map_lang_filter_btmap.is_none());
873 }
874}