mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 17:26:36 -04:00
Merge 1cbcd244cb
into 04a0b10a15
This commit is contained in:
commit
9e813574f4
9 changed files with 161 additions and 44 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -3555,6 +3555,7 @@ dependencies = [
|
||||||
name = "launcher"
|
name = "launcher"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anki_i18n",
|
||||||
"anki_io",
|
"anki_io",
|
||||||
"anki_process",
|
"anki_process",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -3563,6 +3564,7 @@ dependencies = [
|
||||||
"embed-resource",
|
"embed-resource",
|
||||||
"libc",
|
"libc",
|
||||||
"libc-stdhandle",
|
"libc-stdhandle",
|
||||||
|
"locale_config",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"widestring",
|
"widestring",
|
||||||
"windows 0.61.3",
|
"windows 0.61.3",
|
||||||
|
@ -3702,6 +3704,19 @@ version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "locale_config"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"objc",
|
||||||
|
"objc-foundation",
|
||||||
|
"regex",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
|
@ -4380,6 +4395,26 @@ dependencies = [
|
||||||
"malloc_buf",
|
"malloc_buf",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-foundation"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
|
||||||
|
dependencies = [
|
||||||
|
"block",
|
||||||
|
"objc",
|
||||||
|
"objc_id",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc_id"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
|
||||||
|
dependencies = [
|
||||||
|
"objc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
|
|
|
@ -92,6 +92,7 @@ itertools = "0.14.0"
|
||||||
junction = "1.2.0"
|
junction = "1.2.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
libc-stdhandle = "0.1"
|
libc-stdhandle = "0.1"
|
||||||
|
locale_config = "0.3.0"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
nom = "8.0.0"
|
nom = "8.0.0"
|
||||||
num-format = "0.4.4"
|
num-format = "0.4.4"
|
||||||
|
|
|
@ -2226,7 +2226,7 @@
|
||||||
{
|
{
|
||||||
"authors": "Ibraheem Ahmed <ibraheem@ibraheem.ca>",
|
"authors": "Ibraheem Ahmed <ibraheem@ibraheem.ca>",
|
||||||
"description": "A high performance, zero-copy URL router.",
|
"description": "A high performance, zero-copy URL router.",
|
||||||
"license": "MIT AND BSD-3-Clause",
|
"license": "BSD-3-Clause AND MIT",
|
||||||
"license_file": null,
|
"license_file": null,
|
||||||
"name": "matchit",
|
"name": "matchit",
|
||||||
"repository": "https://github.com/ibraheemdev/matchit"
|
"repository": "https://github.com/ibraheemdev/matchit"
|
||||||
|
@ -4154,7 +4154,7 @@
|
||||||
{
|
{
|
||||||
"authors": "David Tolnay <dtolnay@gmail.com>",
|
"authors": "David Tolnay <dtolnay@gmail.com>",
|
||||||
"description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
|
"description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
|
||||||
"license": "(MIT OR Apache-2.0) AND Unicode-3.0",
|
"license": "(Apache-2.0 OR MIT) AND Unicode-3.0",
|
||||||
"license_file": null,
|
"license_file": null,
|
||||||
"name": "unicode-ident",
|
"name": "unicode-ident",
|
||||||
"repository": "https://github.com/dtolnay/unicode-ident"
|
"repository": "https://github.com/dtolnay/unicode-ident"
|
||||||
|
|
33
ftl/core/launcher.ftl
Normal file
33
ftl/core/launcher.ftl
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
launcher-title = Anki Launcher
|
||||||
|
launcher-press-enter-to-start = Press enter to start Anki.
|
||||||
|
launcher-anki-will-start-shortly = Anki will start shortly.
|
||||||
|
launcher-you-can-close-this-window = You can close this window.
|
||||||
|
launcher-updating-anki = Updating Anki...
|
||||||
|
launcher-latest-anki = Latest Anki (just press Enter)
|
||||||
|
launcher-choose-a-version = Choose a version
|
||||||
|
launcher-sync-project-changes = Sync project changes
|
||||||
|
launcher-keep-existing-version = Keep existing version ({ $current })
|
||||||
|
launcher-revert-to-previous = Revert to previous version ({ $prev })
|
||||||
|
launcher-allow-betas = Allow betas: { $state }
|
||||||
|
launcher-on = on
|
||||||
|
launcher-off = off
|
||||||
|
launcher-cache-downloads = Cache downloads: { $state }
|
||||||
|
launcher-download-mirror = Download mirror: { $state }
|
||||||
|
launcher-uninstall = Uninstall
|
||||||
|
launcher-invalid-input = Invalid input. Please try again.
|
||||||
|
launcher-latest-releases = Latest releases: { $releases }
|
||||||
|
launcher-enter-the-version-you-want = Enter the version you want to install:
|
||||||
|
launcher-versions-before-cant-be-installed = Versions before 2.1.50 can't be installed.
|
||||||
|
launcher-invalid-version = Invalid version.
|
||||||
|
launcher-unable-to-check-for-versions = Unable to check for Anki versions. Please check your internet connection.
|
||||||
|
launcher-checking-for-updates = Checking for updates...
|
||||||
|
launcher-uninstall-confirm = Uninstall Anki's program files? (y/n)
|
||||||
|
launcher-uninstall-cancelled = Uninstall cancelled.
|
||||||
|
launcher-program-files-removed = Program files removed.
|
||||||
|
launcher-remove-all-profiles-confirm = Remove all profiles/cards? (y/n)
|
||||||
|
launcher-user-data-removed = User data removed.
|
||||||
|
launcher-download-mirror-options = Download mirror options:
|
||||||
|
launcher-mirror-no-mirror = No mirror
|
||||||
|
launcher-mirror-china = China
|
||||||
|
launcher-mirror-disabled = Mirror disabled.
|
||||||
|
launcher-mirror-china-enabled = China mirror enabled.
|
|
@ -8,11 +8,13 @@ publish = false
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anki_i18n.workspace = true
|
||||||
anki_io.workspace = true
|
anki_io.workspace = true
|
||||||
anki_process.workspace = true
|
anki_process.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
camino.workspace = true
|
camino.workspace = true
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
|
locale_config.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
||||||
|
|
|
@ -10,6 +10,7 @@ use std::process::Command;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
|
use anki_i18n::I18n;
|
||||||
use anki_io::copy_file;
|
use anki_io::copy_file;
|
||||||
use anki_io::create_dir_all;
|
use anki_io::create_dir_all;
|
||||||
use anki_io::modified_time;
|
use anki_io::modified_time;
|
||||||
|
@ -31,6 +32,7 @@ use crate::platform::respawn_launcher;
|
||||||
mod platform;
|
mod platform;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
|
tr: I18n,
|
||||||
current_version: Option<String>,
|
current_version: Option<String>,
|
||||||
prerelease_marker: std::path::PathBuf,
|
prerelease_marker: std::path::PathBuf,
|
||||||
uv_install_root: std::path::PathBuf,
|
uv_install_root: std::path::PathBuf,
|
||||||
|
@ -100,7 +102,14 @@ fn run() -> Result<()> {
|
||||||
|
|
||||||
let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?;
|
let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?;
|
||||||
|
|
||||||
|
let locale = locale_config::Locale::user_default().to_string();
|
||||||
|
|
||||||
let mut state = State {
|
let mut state = State {
|
||||||
|
tr: I18n::new(&[if !locale.is_empty() {
|
||||||
|
locale
|
||||||
|
} else {
|
||||||
|
"en".to_owned()
|
||||||
|
}]),
|
||||||
current_version: None,
|
current_version: None,
|
||||||
prerelease_marker: uv_install_root.join("prerelease"),
|
prerelease_marker: uv_install_root.join("prerelease"),
|
||||||
uv_install_root: uv_install_root.clone(),
|
uv_install_root: uv_install_root.clone(),
|
||||||
|
@ -160,7 +169,7 @@ fn run() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top
|
print!("\x1B[2J\x1B[H"); // Clear screen and move cursor to top
|
||||||
println!("\x1B[1mAnki Launcher\x1B[0m\n");
|
println!("\x1B[1m{}\x1B[0m\n", state.tr.launcher_title());
|
||||||
|
|
||||||
ensure_os_supported()?;
|
ensure_os_supported()?;
|
||||||
|
|
||||||
|
@ -178,15 +187,18 @@ fn run() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(unix) && !cfg!(target_os = "macos") {
|
if cfg!(unix) && !cfg!(target_os = "macos") {
|
||||||
println!("\nPress enter to start Anki.");
|
println!("\n{}", state.tr.launcher_press_enter_to_start());
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
let _ = stdin().read_line(&mut input);
|
let _ = stdin().read_line(&mut input);
|
||||||
} else {
|
} else {
|
||||||
// on Windows/macOS, the user needs to close the terminal/console
|
// on Windows/macOS, the user needs to close the terminal/console
|
||||||
// currently, but ideas on how we can avoid this would be good!
|
// currently, but ideas on how we can avoid this would be good!
|
||||||
println!();
|
println!();
|
||||||
println!("Anki will start shortly.");
|
println!("{}", state.tr.launcher_anki_will_start_shortly());
|
||||||
println!("\x1B[1mYou can close this window.\x1B[0m\n");
|
println!(
|
||||||
|
"\x1B[1m{}\x1B[0m\n",
|
||||||
|
state.tr.launcher_you_can_close_this_window()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// respawn the launcher as a disconnected subprocess for normal startup
|
// respawn the launcher as a disconnected subprocess for normal startup
|
||||||
|
@ -258,7 +270,7 @@ fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Re
|
||||||
// Remove sync marker before attempting sync
|
// Remove sync marker before attempting sync
|
||||||
let _ = remove_file(&state.sync_complete_marker);
|
let _ = remove_file(&state.sync_complete_marker);
|
||||||
|
|
||||||
println!("Updating Anki...\n");
|
println!("{}\n", state.tr.launcher_updating_anki());
|
||||||
|
|
||||||
let python_version_trimmed = if state.user_python_version_path.exists() {
|
let python_version_trimmed = if state.user_python_version_path.exists() {
|
||||||
let python_version = read_file(&state.user_python_version_path)?;
|
let python_version = read_file(&state.user_python_version_path)?;
|
||||||
|
@ -440,44 +452,62 @@ fn file_timestamp_secs(path: &std::path::Path) -> i64 {
|
||||||
|
|
||||||
fn get_main_menu_choice(state: &State) -> Result<MainMenuChoice> {
|
fn get_main_menu_choice(state: &State) -> Result<MainMenuChoice> {
|
||||||
loop {
|
loop {
|
||||||
println!("1) Latest Anki (press Enter)");
|
println!("1) {}", state.tr.launcher_latest_anki());
|
||||||
println!("2) Choose a version");
|
println!("2) {}", state.tr.launcher_choose_a_version());
|
||||||
|
|
||||||
if let Some(current_version) = &state.current_version {
|
if let Some(current_version) = &state.current_version {
|
||||||
let normalized_current = normalize_version(current_version);
|
let normalized_current = normalize_version(current_version);
|
||||||
|
|
||||||
if state.pyproject_modified_by_user {
|
if state.pyproject_modified_by_user {
|
||||||
println!("3) Sync project changes");
|
println!("3) {}", state.tr.launcher_sync_project_changes());
|
||||||
} else {
|
} else {
|
||||||
println!("3) Keep existing version ({normalized_current})");
|
println!(
|
||||||
|
"3) {}",
|
||||||
|
state.tr.launcher_keep_existing_version(normalized_current)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prev_version) = &state.previous_version {
|
if let Some(prev_version) = &state.previous_version {
|
||||||
if state.current_version.as_ref() != Some(prev_version) {
|
if state.current_version.as_ref() != Some(prev_version) {
|
||||||
let normalized_prev = normalize_version(prev_version);
|
let normalized_prev = normalize_version(prev_version);
|
||||||
println!("4) Revert to previous version ({normalized_prev})");
|
println!(
|
||||||
|
"4) {}",
|
||||||
|
state.tr.launcher_revert_to_previous(normalized_prev)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
let betas_enabled = state.prerelease_marker.exists();
|
let betas_enabled = state.prerelease_marker.exists();
|
||||||
println!(
|
println!(
|
||||||
"5) Allow betas: {}",
|
"5) {}",
|
||||||
if betas_enabled { "on" } else { "off" }
|
state.tr.launcher_allow_betas(if betas_enabled {
|
||||||
|
state.tr.launcher_on()
|
||||||
|
} else {
|
||||||
|
state.tr.launcher_off()
|
||||||
|
})
|
||||||
);
|
);
|
||||||
let cache_enabled = !state.no_cache_marker.exists();
|
let cache_enabled = !state.no_cache_marker.exists();
|
||||||
println!(
|
println!(
|
||||||
"6) Cache downloads: {}",
|
"6) {}",
|
||||||
if cache_enabled { "on" } else { "off" }
|
state.tr.launcher_cache_downloads(if cache_enabled {
|
||||||
|
state.tr.launcher_on()
|
||||||
|
} else {
|
||||||
|
state.tr.launcher_off()
|
||||||
|
})
|
||||||
);
|
);
|
||||||
let mirror_enabled = is_mirror_enabled(state);
|
let mirror_enabled = is_mirror_enabled(state);
|
||||||
println!(
|
println!(
|
||||||
"7) Download mirror: {}",
|
"7) {}",
|
||||||
if mirror_enabled { "on" } else { "off" }
|
state.tr.launcher_download_mirror(if mirror_enabled {
|
||||||
|
state.tr.launcher_on()
|
||||||
|
} else {
|
||||||
|
state.tr.launcher_off()
|
||||||
|
})
|
||||||
);
|
);
|
||||||
println!();
|
println!();
|
||||||
println!("8) Uninstall");
|
println!("8) {}", state.tr.launcher_uninstall());
|
||||||
print!("> ");
|
print!("> ");
|
||||||
let _ = stdout().flush();
|
let _ = stdout().flush();
|
||||||
|
|
||||||
|
@ -499,7 +529,7 @@ fn get_main_menu_choice(state: &State) -> Result<MainMenuChoice> {
|
||||||
if state.current_version.is_some() {
|
if state.current_version.is_some() {
|
||||||
MainMenuChoice::KeepExisting
|
MainMenuChoice::KeepExisting
|
||||||
} else {
|
} else {
|
||||||
println!("Invalid input. Please try again.\n");
|
println!("{}\n", state.tr.launcher_invalid_input());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,7 +541,7 @@ fn get_main_menu_choice(state: &State) -> Result<MainMenuChoice> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Invalid input. Please try again.\n");
|
println!("{}\n", state.tr.launcher_invalid_input());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
"5" => MainMenuChoice::ToggleBetas,
|
"5" => MainMenuChoice::ToggleBetas,
|
||||||
|
@ -519,7 +549,7 @@ fn get_main_menu_choice(state: &State) -> Result<MainMenuChoice> {
|
||||||
"7" => MainMenuChoice::DownloadMirror,
|
"7" => MainMenuChoice::DownloadMirror,
|
||||||
"8" => MainMenuChoice::Uninstall,
|
"8" => MainMenuChoice::Uninstall,
|
||||||
_ => {
|
_ => {
|
||||||
println!("Invalid input. Please try again.");
|
println!("{}\n", state.tr.launcher_invalid_input());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -534,9 +564,9 @@ fn get_version_kind(state: &State) -> Result<Option<VersionKind>> {
|
||||||
.map(|v| v.as_str())
|
.map(|v| v.as_str())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
println!("Latest releases: {releases_str}");
|
println!("{}", state.tr.launcher_latest_releases(releases_str));
|
||||||
|
|
||||||
println!("Enter the version you want to install:");
|
println!("{}", state.tr.launcher_enter_the_version_you_want());
|
||||||
print!("> ");
|
print!("> ");
|
||||||
let _ = stdout().flush();
|
let _ = stdout().flush();
|
||||||
|
|
||||||
|
@ -560,11 +590,11 @@ fn get_version_kind(state: &State) -> Result<Option<VersionKind>> {
|
||||||
Ok(Some(version_kind))
|
Ok(Some(version_kind))
|
||||||
}
|
}
|
||||||
(None, true) => {
|
(None, true) => {
|
||||||
println!("Versions before 2.1.50 can't be installed.");
|
println!("{}", state.tr.launcher_versions_before_cant_be_installed());
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Invalid version.\n");
|
println!("{}\n", state.tr.launcher_invalid_version());
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,7 +730,7 @@ fn fetch_versions(state: &State) -> Result<Vec<String>> {
|
||||||
let output = match cmd.utf8_output() {
|
let output = match cmd.utf8_output() {
|
||||||
Ok(output) => output,
|
Ok(output) => output,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
print!("Unable to check for Anki versions. Please check your internet connection.\n\n");
|
print!("{}\n\n", state.tr.launcher_unable_to_check_for_versions());
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -709,7 +739,7 @@ fn fetch_versions(state: &State) -> Result<Vec<String>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_releases(state: &State) -> Result<Releases> {
|
fn get_releases(state: &State) -> Result<Releases> {
|
||||||
println!("Checking for updates...");
|
println!("{}", state.tr.launcher_checking_for_updates());
|
||||||
let include_prereleases = state.prerelease_marker.exists();
|
let include_prereleases = state.prerelease_marker.exists();
|
||||||
let all_versions = fetch_versions(state)?;
|
let all_versions = fetch_versions(state)?;
|
||||||
let all_versions = filter_and_normalize_versions(all_versions, include_prereleases);
|
let all_versions = filter_and_normalize_versions(all_versions, include_prereleases);
|
||||||
|
@ -911,7 +941,7 @@ fn get_anki_addons21_path() -> Result<std::path::PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_uninstall(state: &State) -> Result<bool> {
|
fn handle_uninstall(state: &State) -> Result<bool> {
|
||||||
println!("Uninstall Anki's program files? (y/n)");
|
println!("{}", state.tr.launcher_uninstall_confirm());
|
||||||
print!("> ");
|
print!("> ");
|
||||||
let _ = stdout().flush();
|
let _ = stdout().flush();
|
||||||
|
|
||||||
|
@ -920,7 +950,7 @@ fn handle_uninstall(state: &State) -> Result<bool> {
|
||||||
let input = input.trim().to_lowercase();
|
let input = input.trim().to_lowercase();
|
||||||
|
|
||||||
if input != "y" {
|
if input != "y" {
|
||||||
println!("Uninstall cancelled.");
|
println!("{}", state.tr.launcher_uninstall_cancelled());
|
||||||
println!();
|
println!();
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
@ -928,11 +958,11 @@ fn handle_uninstall(state: &State) -> Result<bool> {
|
||||||
// Remove program files
|
// Remove program files
|
||||||
if state.uv_install_root.exists() {
|
if state.uv_install_root.exists() {
|
||||||
anki_io::remove_dir_all(&state.uv_install_root)?;
|
anki_io::remove_dir_all(&state.uv_install_root)?;
|
||||||
println!("Program files removed.");
|
println!("{}", state.tr.launcher_program_files_removed());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
println!("Remove all profiles/cards? (y/n)");
|
println!("{}", state.tr.launcher_remove_all_profiles_confirm());
|
||||||
print!("> ");
|
print!("> ");
|
||||||
let _ = stdout().flush();
|
let _ = stdout().flush();
|
||||||
|
|
||||||
|
@ -942,7 +972,7 @@ fn handle_uninstall(state: &State) -> Result<bool> {
|
||||||
|
|
||||||
if input == "y" && state.anki_base_folder.exists() {
|
if input == "y" && state.anki_base_folder.exists() {
|
||||||
anki_io::remove_dir_all(&state.anki_base_folder)?;
|
anki_io::remove_dir_all(&state.anki_base_folder)?;
|
||||||
println!("User data removed.");
|
println!("{}", state.tr.launcher_user_data_removed());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
@ -1036,9 +1066,9 @@ fn get_mirror_urls(state: &State) -> Result<Option<(String, String)>> {
|
||||||
|
|
||||||
fn show_mirror_submenu(state: &State) -> Result<()> {
|
fn show_mirror_submenu(state: &State) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
println!("Download mirror options:");
|
println!("{}", state.tr.launcher_download_mirror_options());
|
||||||
println!("1) No mirror");
|
println!("1) {}", state.tr.launcher_mirror_no_mirror());
|
||||||
println!("2) China");
|
println!("2) {}", state.tr.launcher_mirror_china());
|
||||||
print!("> ");
|
print!("> ");
|
||||||
let _ = stdout().flush();
|
let _ = stdout().flush();
|
||||||
|
|
||||||
|
@ -1052,14 +1082,14 @@ fn show_mirror_submenu(state: &State) -> Result<()> {
|
||||||
if state.mirror_path.exists() {
|
if state.mirror_path.exists() {
|
||||||
let _ = remove_file(&state.mirror_path);
|
let _ = remove_file(&state.mirror_path);
|
||||||
}
|
}
|
||||||
println!("Mirror disabled.");
|
println!("{}", state.tr.launcher_mirror_disabled());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
"2" => {
|
"2" => {
|
||||||
// Write China mirror URLs
|
// Write China mirror URLs
|
||||||
let china_mirrors = "https://registry.npmmirror.com/-/binary/python-build-standalone/\nhttps://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/";
|
let china_mirrors = "https://registry.npmmirror.com/-/binary/python-build-standalone/\nhttps://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/";
|
||||||
write_file(&state.mirror_path, china_mirrors)?;
|
write_file(&state.mirror_path, china_mirrors)?;
|
||||||
println!("China mirror enabled.");
|
println!("{}", state.tr.launcher_mirror_china_enabled());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
"" => {
|
"" => {
|
||||||
|
@ -1067,7 +1097,7 @@ fn show_mirror_submenu(state: &State) -> Result<()> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Invalid input. Please try again.");
|
println!("{}", state.tr.launcher_invalid_input());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,10 @@ use write_strings::write_strings;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// generate our own requirements
|
// generate our own requirements
|
||||||
let map = get_ftl_data();
|
let mut map = get_ftl_data();
|
||||||
check(&map);
|
check(&map);
|
||||||
let modules = get_modules(&map);
|
let mut modules = get_modules(&map);
|
||||||
write_strings(&map, &modules);
|
write_strings(&map, &modules, "strings.rs");
|
||||||
|
|
||||||
typescript::write_ts_interface(&modules)?;
|
typescript::write_ts_interface(&modules)?;
|
||||||
python::write_py_interface(&modules)?;
|
python::write_py_interface(&modules)?;
|
||||||
|
@ -41,5 +41,15 @@ fn main() -> Result<()> {
|
||||||
write_file_if_changed(path, meta_json)?;
|
write_file_if_changed(path, meta_json)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allow passing in "--cfg launcher" for the launcher
|
||||||
|
println!("cargo::rustc-check-cfg=cfg(launcher)");
|
||||||
|
|
||||||
|
// generate strings for the launcher
|
||||||
|
map.iter_mut()
|
||||||
|
.for_each(|(_, modules)| modules.retain(|module, _| module == "launcher"));
|
||||||
|
modules.retain(|module| module.name == "launcher");
|
||||||
|
write_strings(&map, &modules, "strings_launcher.rs");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,8 @@
|
||||||
|
|
||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
|
|
||||||
|
#[cfg(not(launcher))]
|
||||||
include!(concat!(env!("OUT_DIR"), "/strings.rs"));
|
include!(concat!(env!("OUT_DIR"), "/strings.rs"));
|
||||||
|
|
||||||
|
#[cfg(launcher)]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/strings_launcher.rs"));
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::extract::VariableKind;
|
||||||
use crate::gather::TranslationsByFile;
|
use crate::gather::TranslationsByFile;
|
||||||
use crate::gather::TranslationsByLang;
|
use crate::gather::TranslationsByLang;
|
||||||
|
|
||||||
pub fn write_strings(map: &TranslationsByLang, modules: &[Module]) {
|
pub fn write_strings(map: &TranslationsByLang, modules: &[Module], out_fn: &str) {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
// lang->module map
|
// lang->module map
|
||||||
|
@ -28,14 +28,16 @@ pub fn write_strings(map: &TranslationsByLang, modules: &[Module]) {
|
||||||
write_methods(modules, &mut buf);
|
write_methods(modules, &mut buf);
|
||||||
|
|
||||||
let dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
let dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||||
let path = dir.join("strings.rs");
|
let path = dir.join(out_fn);
|
||||||
fs::write(path, buf).unwrap();
|
fs::write(path, buf).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_methods(modules: &[Module], buf: &mut String) {
|
fn write_methods(modules: &[Module], buf: &mut String) {
|
||||||
buf.push_str(
|
buf.push_str(
|
||||||
r#"
|
r#"
|
||||||
|
#[allow(unused_imports)]
|
||||||
use crate::{I18n,Number};
|
use crate::{I18n,Number};
|
||||||
|
#[allow(unused_imports)]
|
||||||
use fluent::{FluentValue, FluentArgs};
|
use fluent::{FluentValue, FluentArgs};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue