mirror of
https://github.com/flectar/mail.git
synced 2026-11-11 17:57:54 +00:00
chore: merge main
This commit is contained in:
commit
96fc37c361
3 files changed
+88
-9
No files matched your search
+5
-4
@@ -217,10 +217,11 @@ composer added aren't checked. Turn spell checking off with **Check spelling**
|
||||
in Settings → General, which also says when there's nothing to check with.
|
||||
|
||||
On Windows and Linux, the **Spelling** submenu also chooses your languages.
|
||||
Flectar Mail starts with the languages your system is set to, and a word passes
|
||||
when any of the chosen languages has it, so mail in two languages needs no
|
||||
switching. On iOS it checks in your preferred languages, from Settings under
|
||||
General, Language & Region, the same way. On macOS, the system picks the
|
||||
Flectar Mail starts with the display language you chose in Settings, if any,
|
||||
and the languages your system is set to, and a word passes when any of the
|
||||
chosen languages has it, so mail in two languages needs no switching. On iOS it
|
||||
checks in the same display language and your preferred languages, from Settings
|
||||
under General, Language & Region, the same way. On macOS, the system picks the
|
||||
language of what you write from the ones set in System Settings, under
|
||||
Keyboard, Text Input. On Android, the spell checker chosen in Android's
|
||||
settings checks, usually the keyboard's, in the language set there.
|
||||
|
||||
@@ -1175,6 +1175,7 @@ fn apply_language(app: &AppWindow, preference: &str) {
|
||||
language => language,
|
||||
};
|
||||
number_format::set_language(selected);
|
||||
spelling::apply_display_language(language_mode);
|
||||
if let Err(error) = slint::select_bundled_translation(selected) {
|
||||
eprintln!("could not select {selected} translation: {error}");
|
||||
}
|
||||
|
||||
+82
-5
@@ -26,7 +26,7 @@ use std::cell::{Cell, RefCell};
|
||||
use std::ops::Range;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex, PoisonError};
|
||||
|
||||
/// Whether the app has a checker of its own for this platform. A host may bring one too.
|
||||
#[cfg(desktop)]
|
||||
@@ -56,6 +56,10 @@ thread_local! {
|
||||
static SPELLING: RefCell<Option<Rc<Spelling>>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// The display language the user chose in Settings, such as "es", which is checked first when
|
||||
/// they chose no spelling languages. `None` while the app follows the system's language.
|
||||
static DISPLAY_LANGUAGE: Mutex<Option<String>> = Mutex::new(None);
|
||||
|
||||
/// A language spelling can be checked in.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct Language {
|
||||
@@ -271,6 +275,23 @@ pub(crate) fn apply_settings(enabled: bool, languages: Option<Vec<String>>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The display language as set in Settings: "system", or a code such as "es". Open messages are
|
||||
/// checked again when that changes the languages checked by default.
|
||||
pub(crate) fn apply_display_language(language: &str) {
|
||||
let display = (language != "system").then(|| language.to_owned());
|
||||
let changed = {
|
||||
let mut current = DISPLAY_LANGUAGE
|
||||
.lock()
|
||||
.unwrap_or_else(PoisonError::into_inner);
|
||||
let changed = *current != display;
|
||||
*current = display;
|
||||
changed
|
||||
};
|
||||
if changed && service().is_some_and(|spelling| spelling.chosen.borrow().is_none()) {
|
||||
crate::composer::spelling_changed();
|
||||
}
|
||||
}
|
||||
|
||||
/// Show the spelling settings in the main window's settings.
|
||||
pub(crate) fn apply_to_window(app: &crate::AppWindow) {
|
||||
let Some(spelling) = service() else {
|
||||
@@ -461,8 +482,8 @@ pub(crate) struct Checked {
|
||||
pub(crate) found: Vec<Range<usize>>,
|
||||
}
|
||||
|
||||
/// The installed languages that are checked: those `chosen`, or the ones matching the system's
|
||||
/// languages when the user chose none.
|
||||
/// The installed languages that are checked: those `chosen`, or the ones matching the display
|
||||
/// language and the system's languages when the user chose none.
|
||||
fn active_languages(installed: &[Language], chosen: Option<&[String]>) -> Vec<String> {
|
||||
match chosen {
|
||||
Some(chosen) => chosen
|
||||
@@ -474,9 +495,40 @@ fn active_languages(installed: &[Language], chosen: Option<&[String]>) -> Vec<St
|
||||
}
|
||||
}
|
||||
|
||||
/// The installed languages matching the system's languages, as checked when the user chose none.
|
||||
/// The installed languages matching the display language and the system's languages, as checked
|
||||
/// when the user chose none.
|
||||
pub(crate) fn system_languages(installed: &[Language]) -> Vec<String> {
|
||||
default_languages(installed, &sys_locale::get_locales().collect::<Vec<_>>())
|
||||
let display = DISPLAY_LANGUAGE
|
||||
.lock()
|
||||
.unwrap_or_else(PoisonError::into_inner)
|
||||
.clone();
|
||||
let locales = default_locales(display.as_deref(), sys_locale::get_locales().collect());
|
||||
default_languages(installed, &locales)
|
||||
}
|
||||
|
||||
/// The locales spelling starts with: the display language the user chose first, then the
|
||||
/// system's. A system locale in the same language goes first in its place, so its region is
|
||||
/// kept: English shown on a British system stays British English.
|
||||
fn default_locales(display: Option<&str>, mut system: Vec<String>) -> Vec<String> {
|
||||
let Some(display) = display else {
|
||||
return system;
|
||||
};
|
||||
let language = |locale: &str| {
|
||||
locale
|
||||
.split(['-', '_'])
|
||||
.next()
|
||||
.unwrap_or_default()
|
||||
.to_ascii_lowercase()
|
||||
};
|
||||
let wanted = language(display);
|
||||
match system.iter().position(|locale| language(locale) == wanted) {
|
||||
Some(index) => {
|
||||
let same = system.remove(index);
|
||||
system.insert(0, same);
|
||||
}
|
||||
None => system.insert(0, display.to_owned()),
|
||||
}
|
||||
system
|
||||
}
|
||||
|
||||
/// The installed languages matching the system's, one for each of its languages: the same
|
||||
@@ -770,6 +822,31 @@ mod tests {
|
||||
assert_eq!(defaults(&["es"], &["es-MX"]), ["es"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_display_language_the_user_chose_comes_first() {
|
||||
let installed = installed(&["en_GB", "en_US", "es_ES", "es_MX", "ru_RU"]);
|
||||
let starting = |display: Option<&str>, system: &[&str]| {
|
||||
let system = system.iter().map(|locale| (*locale).to_owned()).collect();
|
||||
default_languages(&installed, &default_locales(display, system))
|
||||
};
|
||||
// Following the system, only its languages.
|
||||
assert_eq!(starting(None, &["en-US"]), ["en_US"]);
|
||||
// Spanish shown on an English system checks both, Spanish first.
|
||||
assert_eq!(starting(Some("es"), &["en-US"]), ["es_ES", "en_US"]);
|
||||
// English shown on a British system stays British English.
|
||||
assert_eq!(
|
||||
starting(Some("en"), &["es-ES", "en-GB"]),
|
||||
["en_GB", "es_ES"]
|
||||
);
|
||||
// A display language without a dictionary changes nothing.
|
||||
assert_eq!(starting(Some("zh_Hans"), &["en-US"]), ["en_US"]);
|
||||
|
||||
apply_display_language("es");
|
||||
assert_eq!(*DISPLAY_LANGUAGE.lock().unwrap(), Some("es".to_owned()));
|
||||
apply_display_language("system");
|
||||
assert_eq!(*DISPLAY_LANGUAGE.lock().unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quotes_code_and_the_inserted_signature_are_left_out() {
|
||||
let signature = "\n\n-- \nDean Tirok";
|
||||
|
||||
Reference in new issue
Block a user