From 8c6cbe09a021e2f1daa2314a1cf3af5fb73830a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Dvo=C5=99=C3=A1k?= <150935816+Dvorinka@users.noreply.github.com> Date: Mon, 21 Sep 2026 12:59:16 +0200 Subject: [PATCH] feat: in-app updates (GitHub desktop, Play Android) + i18n fixes (#241) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(desktop): share dialog on Linux/macOS via cloudreve://share deeplink The Explorer context menu is Windows-only; the share dialog itself is cross-platform but had no trigger elsewhere. cloudreve://share/ now routes through the same ManagerCommand::ShareLink pipeline, and desktop/linux/ ships a cloudreve-share helper plus Nautilus script and Dolphin service-menu glue so file managers can reach it. OAuth callback deeplinks are unaffected. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(desktop): don't drop share deep links at startup Queue cloudreve://share/ links arriving before APP_STATE init and drain after the sync service is up; also scan argv for a cloudreve:// URL so a cold start launched by the file manager still opens the share dialog. Nautilus script now reads NAUTILUS_SCRIPT_SELECTED_FILE_PATHS per line so paths with spaces survive. * feat(desktop): macOS share helper + file-manager install docs cloudreve-share for macOS (open cloudreve://share/...) plus Shortcuts Quick Action recipe for Finder. READMEs document Nautilus, Dolphin, and Finder installation. * feat: in-app updates (GitHub desktop, Play Android) + i18n fixes Desktop (GitHub builds): - tauri-plugin-updater wired to releases/latest/latest.json; minisign keypair generated, private key in TAURI_SIGNING_PRIVATE_KEY secrets - Startup check prompts once per version (config.prompted_update_version); About page gains a manual 'Check for updates' entry - New /update window: version + notes -> in-app download overlay with progress events -> install -> restart via app.restart() - Release workflow signs updater artifacts, uploads .sig/.app.tar.gz/ .AppImage.tar.gz, and a new updater-manifest job emits latest.json (desktop/scripts/gen_latest_json.py); tauri-action path signs natively - Desktop version bumped 0.2.0 -> 0.3.0 so released updates register Android (Play builds): - Play in-app update check on resume -> in-app dialog -> official IMMEDIATE update sheet (app-update-ktx); once per version via prefs - targetSdk/compileSdk 36 + AGP 8.9.3 (Play requires >= 36); bumped to versionCode 2 / 1.1.0 — uploaded and published on internal track i18n: - Desktop: translated share.* + settings.notificationSettings into all 10 non-English locales; added update.* everywhere; popup time strings (justNow/minutesAgo/...) localized via i18n.t in utils - Frontend: added 4 keys missing from en-US (raw-key rendering): modals.anonymous, modals.shareTargetsCount, common.save, file.selectUser (+ event.allEventTypes for zh-CN); fixed two mistyped keys (uplaoder->uploader, uploader:->application:uploader) --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/desktop-release.yml | 4 + .github/workflows/release.yml | 25 +- android/app/build.gradle.kts | 13 +- android/app/src/main/AndroidManifest.xml | 2 +- .../org/cloudreve/android/CloudreveApp.kt | 4 + .../org/cloudreve/android/MainActivity.kt | 63 +++++ android/build.gradle.kts | 2 +- desktop/crates/cloudreve-sync/src/config.rs | 4 + desktop/scripts/gen_latest_json.py | 73 ++++++ desktop/src-tauri/Cargo.toml | 3 +- desktop/src-tauri/src/commands.rs | 158 ++++++++++++ desktop/src-tauri/src/lib.rs | 28 +++ desktop/src-tauri/tauri.conf.json | 24 +- desktop/ui/public/locales/de/common.json | 66 +++-- desktop/ui/public/locales/en-US/common.json | 22 +- desktop/ui/public/locales/es/common.json | 64 +++-- desktop/ui/public/locales/fr/common.json | 64 +++-- desktop/ui/public/locales/it/common.json | 62 +++-- desktop/ui/public/locales/ja/common.json | 66 +++-- desktop/ui/public/locales/ko/common.json | 64 +++-- desktop/ui/public/locales/pl/common.json | 66 +++-- desktop/ui/public/locales/ru/common.json | 64 +++-- desktop/ui/public/locales/zh-CN/common.json | 22 +- desktop/ui/public/locales/zh-TW/common.json | 64 +++-- desktop/ui/src/App.tsx | 2 + desktop/ui/src/pages/Update.tsx | 227 ++++++++++++++++++ desktop/ui/src/pages/popup/utils.ts | 12 +- .../ui/src/pages/settings/AboutSection.tsx | 18 +- .../public/locales/ar-AR/application.json | 13 +- frontend/public/locales/ar-AR/common.json | 5 +- frontend/public/locales/ar-AR/dashboard.json | 6 +- .../public/locales/de-DE/application.json | 14 +- frontend/public/locales/de-DE/common.json | 5 +- frontend/public/locales/de-DE/dashboard.json | 8 +- .../public/locales/en-US/application.json | 9 +- frontend/public/locales/en-US/common.json | 5 +- frontend/public/locales/en-US/dashboard.json | 3 +- .../public/locales/es-ES/application.json | 14 +- frontend/public/locales/es-ES/common.json | 5 +- frontend/public/locales/es-ES/dashboard.json | 8 +- .../public/locales/fr-FR/application.json | 14 +- frontend/public/locales/fr-FR/common.json | 5 +- frontend/public/locales/fr-FR/dashboard.json | 8 +- .../public/locales/it-IT/application.json | 14 +- frontend/public/locales/it-IT/common.json | 5 +- frontend/public/locales/it-IT/dashboard.json | 8 +- .../public/locales/ja-JP/application.json | 13 +- frontend/public/locales/ja-JP/common.json | 5 +- frontend/public/locales/ja-JP/dashboard.json | 7 +- .../public/locales/ko-KR/application.json | 14 +- frontend/public/locales/ko-KR/common.json | 5 +- frontend/public/locales/ko-KR/dashboard.json | 8 +- .../public/locales/pl-PL/application.json | 13 +- frontend/public/locales/pl-PL/common.json | 5 +- frontend/public/locales/pl-PL/dashboard.json | 5 +- .../public/locales/pt-BR/application.json | 14 +- frontend/public/locales/pt-BR/common.json | 5 +- frontend/public/locales/pt-BR/dashboard.json | 8 +- .../public/locales/ru-RU/application.json | 14 +- frontend/public/locales/ru-RU/common.json | 5 +- frontend/public/locales/ru-RU/dashboard.json | 8 +- .../public/locales/zh-CN/application.json | 6 +- frontend/public/locales/zh-CN/common.json | 5 +- frontend/public/locales/zh-CN/dashboard.json | 6 +- .../public/locales/zh-TW/application.json | 13 +- frontend/public/locales/zh-TW/common.json | 5 +- frontend/public/locales/zh-TW/dashboard.json | 7 +- .../src/component/Pages/Tasks/TaskProps.tsx | 2 +- frontend/src/component/Uploader/Uploader.tsx | 2 +- 69 files changed, 1267 insertions(+), 343 deletions(-) create mode 100644 desktop/scripts/gen_latest_json.py create mode 100644 desktop/ui/src/pages/Update.tsx diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml index abe917dc..d8a72b6b 100644 --- a/.github/workflows/desktop-release.yml +++ b/.github/workflows/desktop-release.yml @@ -51,6 +51,10 @@ jobs: - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # With the signing key set, tauri-action signs updater artifacts and + # publishes latest.json on the release — the in-app updater feed. + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} with: projectPath: desktop tagName: ${{ github.ref_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 98bddeca..b51555ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,6 +77,9 @@ jobs: - name: Build desktop bundles working-directory: desktop + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} run: cargo tauri build - name: Publish bundles to GitHub release @@ -92,8 +95,9 @@ jobs: done gh release view "$TAG" >/dev/null FILES=$(find desktop/target/release/bundle -type f \ - \( -name '*.deb' -o -name '*.AppImage' -o -name '*.msi' \ - -o -name '*.exe' -o -name '*.dmg' -o -name '*.app.tar.gz' \)) + \( -name '*.deb' -o -name '*.AppImage' -o -name '*.AppImage.tar.gz' \ + -o -name '*.msi' -o -name '*.exe' -o -name '*.dmg' \ + -o -name '*.app.tar.gz' -o -name '*.sig' \)) [ -n "$FILES" ] echo "$FILES" | while read -r f; do name="cloudreve-desktop-$(basename "$f")" @@ -101,6 +105,23 @@ jobs: gh release upload "$TAG" "$name" --clobber done + updater-manifest: + needs: desktop + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build and publish latest.json + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.ref_name }} + run: | + set -e + mkdir -p sigs + gh release download "$TAG" -p '*.sig' -D sigs --clobber + python3 desktop/scripts/gen_latest_json.py "$TAG" sigs latest.json + gh release upload "$TAG" latest.json --clobber + android: runs-on: ubuntu-latest defaults: diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index e0d60583..464cd00a 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -7,14 +7,14 @@ plugins { android { namespace = "org.cloudreve.android" - compileSdk = 35 + compileSdk = 36 defaultConfig { applicationId = "com.tdvorak.cloudreve" minSdk = 28 - targetSdk = 35 - versionCode = 1 - versionName = "1.0.0" + targetSdk = 36 + versionCode = 2 + versionName = "1.1.0" } signingConfigs { @@ -94,4 +94,9 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") implementation("io.coil-kt:coil-compose:2.7.0") + + // Play in-app updates — no-op on sideloaded/non-Play installs + implementation("com.google.android.play:app-update-ktx:2.1.0") + // ActivityResult API (update flow launcher) needs fragment >= 1.3.0 + implementation("androidx.fragment:fragment-ktx:1.8.5") } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5a4dba39..2b086538 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ + android:targetSdkVersion="36" /> diff --git a/android/app/src/main/java/org/cloudreve/android/CloudreveApp.kt b/android/app/src/main/java/org/cloudreve/android/CloudreveApp.kt index 0d8b1b73..dad53090 100644 --- a/android/app/src/main/java/org/cloudreve/android/CloudreveApp.kt +++ b/android/app/src/main/java/org/cloudreve/android/CloudreveApp.kt @@ -1,6 +1,7 @@ package org.cloudreve.android import android.app.Application +import com.google.android.play.core.appupdate.AppUpdateInfo import kotlinx.coroutines.flow.MutableStateFlow import org.cloudreve.android.api.ApiClient import org.cloudreve.android.data.CameraUploadSettings @@ -16,6 +17,9 @@ class CloudreveApp : Application() { data class OAuthCallback(val code: String, val state: String) val oauthCallback = MutableStateFlow(null) + /** Pending Play in-app update, set by MainActivity's update check. */ + val playUpdateAvailable = MutableStateFlow(null) + lateinit var sessionManager: SessionManager private set lateinit var apiClient: ApiClient diff --git a/android/app/src/main/java/org/cloudreve/android/MainActivity.kt b/android/app/src/main/java/org/cloudreve/android/MainActivity.kt index e1e156b3..8cd8d5c4 100644 --- a/android/app/src/main/java/org/cloudreve/android/MainActivity.kt +++ b/android/app/src/main/java/org/cloudreve/android/MainActivity.kt @@ -6,6 +6,11 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.result.contract.ActivityResultContracts +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.appupdate.AppUpdateOptions +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.UpdateAvailability import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -18,6 +23,9 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import kotlinx.coroutines.launch import org.cloudreve.android.api.RefreshTokenRequest import org.cloudreve.android.ui.files.FilesScreen @@ -30,6 +38,9 @@ import org.cloudreve.android.work.UploadWorker class MainActivity : ComponentActivity() { + private val updateLauncher = + registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -37,6 +48,39 @@ class MainActivity : ComponentActivity() { setContent { App() } } + override fun onResume() { + super.onResume() + checkForPlayUpdate() + } + + // Ask Play for an update; if one is available and this install came from + // Play, surface the in-app prompt once per version, then hand off to the + // official IMMEDIATE update sheet. + private fun checkForPlayUpdate() { + val manager = AppUpdateManagerFactory.create(this) + manager.appUpdateInfo.addOnSuccessListener { info -> + if (info.updateAvailability() != UpdateAvailability.UPDATE_AVAILABLE || + !info.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + ) return@addOnSuccessListener + val version = info.availableVersionCode() + val prefs = getSharedPreferences("updates", MODE_PRIVATE) + if (prefs.getInt("prompted_version", -1) == version) return@addOnSuccessListener + prefs.edit().putInt("prompted_version", version).apply() + (application as CloudreveApp).playUpdateAvailable.value = info + } + } + + fun launchPlayUpdate() { + val info = (application as CloudreveApp).playUpdateAvailable.value ?: return + runCatching { + AppUpdateManagerFactory.create(this).startUpdateFlowForResult( + info, + updateLauncher, + AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build(), + ) + } + } + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) handleShareIntent(intent) @@ -103,6 +147,25 @@ fun App() { } CloudreveTheme { + val updateInfo by app.playUpdateAvailable.collectAsState() + if (updateInfo != null) { + AlertDialog( + onDismissRequest = { app.playUpdateAvailable.value = null }, + title = { Text("Update available") }, + text = { Text("A new version of Cloudreve Mobile is available on Google Play.") }, + confirmButton = { + TextButton(onClick = { + app.playUpdateAvailable.value = null + (context as? MainActivity)?.launchPlayUpdate() + }) { Text("Update") } + }, + dismissButton = { + TextButton(onClick = { app.playUpdateAvailable.value = null }) { + Text("Later") + } + }, + ) + } when (loggedIn) { null -> Unit // session store still loading; keep frame empty false -> NavHost(navController = nav, startDestination = "login") { diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 70dfb636..e795862a 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.application") version "8.7.3" apply false + id("com.android.application") version "8.9.3" apply false id("org.jetbrains.kotlin.android") version "2.0.21" apply false id("org.jetbrains.kotlin.plugin.compose") version "2.0.21" apply false id("org.jetbrains.kotlin.plugin.serialization") version "2.0.21" apply false diff --git a/desktop/crates/cloudreve-sync/src/config.rs b/desktop/crates/cloudreve-sync/src/config.rs index 9fbf4fa1..37ea40e8 100644 --- a/desktop/crates/cloudreve-sync/src/config.rs +++ b/desktop/crates/cloudreve-sync/src/config.rs @@ -66,6 +66,9 @@ pub struct AppConfig { pub hide_tray_icon: bool, /// Language/locale setting (e.g., "en-US", "zh-CN"). None means use system default. pub language: Option, + /// Last app-update version the user was already prompted about. A new + /// version still prompts once; dismissing snoozes until the next release. + pub prompted_update_version: Option, } impl Default for AppConfig { @@ -81,6 +84,7 @@ impl Default for AppConfig { sync_delay_seconds: 0, hide_tray_icon: false, language: None, + prompted_update_version: None, } } } diff --git a/desktop/scripts/gen_latest_json.py b/desktop/scripts/gen_latest_json.py new file mode 100644 index 00000000..3aa79024 --- /dev/null +++ b/desktop/scripts/gen_latest_json.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""Generate the Tauri updater manifest (latest.json) from release .sig assets. + +Usage: gen_latest_json.py + +Scans for `*.sig` files downloaded from the GitHub release, maps each +to its updater platform, and emits the manifest the tauri-plugin-updater +expects. Asset URLs point at the release's renamed `cloudreve-desktop-*` +assets. The version is read from desktop/src-tauri/tauri.conf.json — desktop +has its own semver, independent of the repo's server release tags. +""" +import json +import os +import sys + +PLATFORM_MAP = [ + ("_x64-setup.exe", "windows-x86_64"), + ("_arm64-setup.exe", "windows-aarch64"), + ("_aarch64.app.tar.gz", "darwin-aarch64"), + ("_x64.app.tar.gz", "darwin-x86_64"), + ("amd64.AppImage.tar.gz", "linux-x86_64"), + ("aarch64.AppImage.tar.gz", "linux-aarch64"), +] + +REPO = "Dvorinka/cloudreve" + + +def main() -> int: + tag, sig_dir, out_path = sys.argv[1], sys.argv[2], sys.argv[3] + + conf = json.load(open("desktop/src-tauri/tauri.conf.json")) + version = conf["version"] + + platforms = {} + for fname in sorted(os.listdir(sig_dir)): + if not fname.endswith(".sig"): + continue + # Release assets are renamed to `cloudreve-desktop-` at + # upload time, so the downloaded .sig name already is the asset name. + asset = fname[: -len(".sig")] + for marker, platform in PLATFORM_MAP: + if marker in asset: + signature = open(os.path.join(sig_dir, fname)).read().strip() + if not signature: + print(f"warning: empty signature in {fname}, skipping") + break + platforms[platform] = { + "signature": signature, + "url": ( + f"https://github.com/{REPO}/releases/download/{tag}/" + f"{asset}" + ), + } + break + + if not platforms: + print("error: no updater artifacts found", file=sys.stderr) + return 1 + + manifest = { + "version": version, + "notes": f"Cloudreve Desktop {version}", + "pub_date": "", + "platforms": platforms, + } + with open(out_path, "w") as f: + json.dump(manifest, f, indent=2) + print(f"latest.json: version={version}, platforms={sorted(platforms)}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 4787d51d..10f5ae27 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cloudreve-desktop" -version = "0.2.0" +version = "0.3.0" description = "Cloudreve Desktop Sync Client" authors = ["cloudreve"] license = "" @@ -44,6 +44,7 @@ tauri-plugin-deep-link = "2.4.9" tauri-plugin-dialog = "2.7.1" tauri-plugin-prevent-default = "4" tauri-plugin-os = "2" +tauri-plugin-updater = "2" tauri-plugin-frame = "1.1.6" [target.'cfg(windows)'.dependencies.windows] diff --git a/desktop/src-tauri/src/commands.rs b/desktop/src-tauri/src/commands.rs index dfdfe0c6..bb489d6d 100644 --- a/desktop/src-tauri/src/commands.rs +++ b/desktop/src-tauri/src/commands.rs @@ -1471,3 +1471,161 @@ pub async fn open_log_folder() -> CommandResult<()> { showfile::show_path_in_file_manager(format!("{}\\", log_dir.display())); Ok(()) } + +/// Metadata about an available app update, returned to the frontend. +#[derive(serde::Serialize)] +pub struct UpdateInfo { + pub version: String, + pub current_version: String, + pub notes: Option, + pub date: Option, +} + +/// Progress payload emitted on `update-progress` while an update downloads. +#[derive(serde::Serialize, Clone)] +struct UpdateProgress { + downloaded: u64, + total: Option, +} + +/// Check the configured update endpoint (GitHub releases `latest.json`) for a +/// newer desktop build. Returns `None` when already up to date or when no +/// signed updater artifact exists for this platform. +#[tauri::command] +pub async fn check_update(app: AppHandle) -> CommandResult> { + use tauri_plugin_updater::UpdaterExt; + + let update = app + .updater() + .map_err(|e| e.to_string())? + .check() + .await + .map_err(|e| e.to_string())?; + + Ok(update.map(|u| UpdateInfo { + version: u.version.clone(), + current_version: u.current_version.clone(), + notes: u.body.clone(), + date: u.date.map(|d| d.to_string()), + })) +} + +/// Download the pending update, emit `update-progress` events, and install it. +/// The frontend should call `restart_app` afterwards. +#[tauri::command] +pub async fn install_update(app: AppHandle) -> CommandResult<()> { + use tauri::Emitter; + use tauri_plugin_updater::UpdaterExt; + + let update = app + .updater() + .map_err(|e| e.to_string())? + .check() + .await + .map_err(|e| e.to_string())? + .ok_or_else(|| "No update available".to_string())?; + + let app2 = app.clone(); + let app3 = app.clone(); + update + .download_and_install( + move |chunk_len, content_len| { + let _ = app2.emit( + "update-progress", + UpdateProgress { + downloaded: chunk_len as u64, + total: content_len, + }, + ); + }, + move || { + let _ = app3.emit("update-finished", ()); + }, + ) + .await + .map_err(|e| e.to_string())?; + + Ok(()) +} + +/// Restart the application (used after a successful update install). +#[tauri::command] +pub fn restart_app(app: AppHandle) { + app.restart(); +} + +/// Show or create the in-app update window. The window itself runs the update +/// check so it works both for automatic prompts and manual "Check for +/// updates" from the About page. +pub fn show_update_window_impl(app: &AppHandle) { + // One update window at a time — refocus instead of stacking dialogs. + if let Some(window) = app.get_webview_window("update") { + let _ = window.unminimize(); + let _ = window.show(); + let _ = window.set_focus(); + return; + } + + let url_path = get_url_with_lang("index.html#/update"); + + #[cfg(windows)] + let effects = WindowEffectsConfig { + effects: vec![WindowEffect::Mica, WindowEffect::Acrylic], + state: None, + radius: None, + color: None, + }; + + let builder = WebviewWindowBuilder::new(app, "update", WebviewUrl::App(url_path.into())) + .title("Software Update") + .inner_size(460.0, 380.0) + .resizable(false) + .visible(false) + .decorations(false) + .minimizable(false); + + #[cfg(not(windows))] + let builder = builder.background_color(Color(255, 255, 255, 255)); + + #[cfg(windows)] + let builder = builder.transparent(true); + + #[cfg(target_os = "macos")] + let builder = builder + .title_bar_style(TitleBarStyle::Overlay) + .hidden_title(true); + + let Some(builder) = apply_default_window_icon(builder, app, "update") else { + return; + }; + + match builder.build() { + Ok(window) => { + #[cfg(target_os = "macos")] + update_dock_on_window_close(&window); + + #[cfg(windows)] + { + let _ = window.set_effects(effects); + } + + move_window_safely(&window, Position::Center, "update"); + #[cfg(windows)] + let _ = window.create_overlay_titlebar(); + let _ = window.show(); + let _ = window.set_focus(); + #[cfg(target_os = "macos")] + crate::update_dock_visibility(app); + } + Err(e) => { + tracing::error!(target: "main", error = %e, "Failed to create update window"); + } + } +} + +/// Open the in-app update window (Settings → About → "Check for updates"). +#[tauri::command] +pub async fn show_update_window(app: AppHandle) -> CommandResult<()> { + show_update_window_impl(&app); + Ok(()) +} diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 5da95079..5972445e 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -190,6 +190,29 @@ async fn init_sync_service(app: AppHandle) -> anyhow::Result<()> { event_broadcaster.no_drive(); } + // Check GitHub releases for a newer desktop build in the background. The + // prompt is shown once per version; dismissing it snoozes until the next + // release. Failures (offline, no updater artifact for this platform) are + // silent — the About page still offers a manual check. + { + let app = app.clone(); + tauri::async_runtime::spawn(async move { + use tauri_plugin_updater::UpdaterExt; + + tokio::time::sleep(std::time::Duration::from_secs(8)).await; + let Ok(updater) = app.updater() else { return }; + let Ok(Some(update)) = updater.check().await else { return }; + + let config = ConfigManager::get().get_config(); + if config.prompted_update_version.as_deref() == Some(update.version.as_str()) { + return; + } + let _ = ConfigManager::get() + .update(|c| c.prompted_update_version = Some(update.version.clone())); + commands::show_update_window_impl(&app); + }); + } + tracing::info!(target: "main", "Tauri application setup complete"); Ok(()) @@ -472,6 +495,7 @@ pub fn run() { builder .plugin(tauri_plugin_deep_link::init()) + .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_prevent_default::debug()) @@ -532,6 +556,10 @@ pub fn run() { commands::get_file_icon, commands::show_file_in_explorer, commands::create_share, + commands::check_update, + commands::install_update, + commands::restart_app, + commands::show_update_window, commands::show_add_drive_window, commands::show_reauthorize_window, commands::show_settings_window, diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 1813986b..f51e8e5d 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Cloudreve", - "version": "0.2.0", + "version": "0.3.0", "identifier": "cloudreve.desktop", "build": { "frontendDist": "../ui/dist", @@ -41,7 +41,9 @@ "targets": "all", "linux": { "deb": { - "depends": ["fuse3"] + "depends": [ + "fuse3" + ] } }, "icon": [ @@ -50,13 +52,25 @@ "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" - ] + ], + "createUpdaterArtifacts": true }, "plugins": { "deep-link": { "desktop": { - "schemes": ["cloudreve"] + "schemes": [ + "cloudreve" + ] + } + }, + "updater": { + "endpoints": [ + "https://github.com/Dvorinka/cloudreve/releases/latest/download/latest.json" + ], + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDIzRUE2QkM3NzJDQ0QxRjAKUldUdzBjeHl4MnZxSXloWjRaRmtqMXZkcXN1UlRYdk1PanliYis0WDlqWEdWczQ1RHZBcHMvV2cK", + "windows": { + "installMode": "passive" } } } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/de/common.json b/desktop/ui/public/locales/de/common.json index 8a6e0304..acd5c7bb 100644 --- a/desktop/ui/public/locales/de/common.json +++ b/desktop/ui/public/locales/de/common.json @@ -54,14 +54,18 @@ "processing": "Verarbeitung...", "conflicts": "Konflikte", "conflictStatus": "{{count}} Konflikt(e) erfordern Aufmerksamkeit", - "conflictDescription": "Die lokale Datei steht im Konflikt mit der Remote-Version", + "conflictDescription": "Lokale Datei steht im Konflikt mit der Remote-Version", "resolveConflict": "Konflikt lösen", "resolving": "Wird gelöst...", "keepRemote": "Remote-Version behalten", "overwriteRemote": "Remote-Version überschreiben", "saveAsNew": "Als neue Datei speichern", "keepAllRemote": "Alle Remote-Versionen behalten", - "overwriteAllRemote": "Alle Remote-Versionen überschreiben" + "overwriteAllRemote": "Alle Remote-Versionen überschreiben", + "justNow": "Gerade eben", + "minutesAgo": "vor {{count}} Min.", + "hoursAgo": "vor {{count}} Std.", + "daysAgo": "vor {{count}} Tagen" }, "settings": { "title": "Einstellungen", @@ -138,26 +142,40 @@ "yearsAgo": "Vor {{value}} Jahren" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", - "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "title": "Freigabelink für {{name}}", + "linkReady": "Ihr Freigabelink ist bereit:", + "permission": "Freigabeberechtigungen", + "permViewDownload": "Ansehen + Herunterladen", + "permPreviewOnly": "Nur Vorschau (kein Download)", + "permAllowEdit": "Ansehen + Herunterladen + Bearbeiten", + "permUploadOnly": "Nur hochladen (Abgabebox)", + "expires": "Läuft ab", + "expire_never": "Läuft nie ab", + "expire_oneHour": "1 Stunde", + "expire_oneDay": "1 Tag", + "expire_sevenDays": "7 Tage", + "expire_thirtyDays": "30 Tage", + "passwordProtect": "Mit Passwort schützen", + "password": "Passwort", + "downloadLimit": "Download-Limit", + "unlimited": "Unbegrenzt", + "create": "Freigabe erstellen", + "cancel": "Abbrechen", + "done": "Fertig", + "copied": "Link in die Zwischenablage kopiert" + }, + "update": { + "title": "Software-Update", + "available": "Version {{version}} ist verfügbar (aktuell {{current}}).", + "downloading": "Update wird heruntergeladen…", + "ready": "Update installiert — zum Anwenden neu starten.", + "upToDate": "Sie sind auf dem neuesten Stand.", + "failed": "Update-Prüfung fehlgeschlagen.", + "updateNow": "Jetzt aktualisieren", + "later": "Später", + "restart": "Jetzt neu starten", + "close": "Schließen", + "checkNow": "Nach Updates suchen", + "downloadManually": "Manuell herunterladen" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/en-US/common.json b/desktop/ui/public/locales/en-US/common.json index b8c73f0f..413c4d3f 100644 --- a/desktop/ui/public/locales/en-US/common.json +++ b/desktop/ui/public/locales/en-US/common.json @@ -61,7 +61,11 @@ "overwriteRemote": "Overwrite remote", "saveAsNew": "Save as new", "keepAllRemote": "Keep all remote", - "overwriteAllRemote": "Overwrite all remote" + "overwriteAllRemote": "Overwrite all remote", + "justNow": "Just now", + "minutesAgo": "{{count}}m ago", + "hoursAgo": "{{count}}h ago", + "daysAgo": "{{count}}d ago" }, "settings": { "title": "Settings", @@ -159,5 +163,19 @@ "cancel": "Cancel", "done": "Done", "copied": "Link copied to clipboard" + }, + "update": { + "title": "Software Update", + "available": "Version {{version}} is available (you have {{current}}).", + "downloading": "Downloading update…", + "ready": "Update installed — restart to apply.", + "upToDate": "You're up to date.", + "failed": "Update check failed.", + "updateNow": "Update now", + "later": "Later", + "restart": "Restart now", + "close": "Close", + "checkNow": "Check for updates", + "downloadManually": "Download manually" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/es/common.json b/desktop/ui/public/locales/es/common.json index a68a318d..ff0eea71 100644 --- a/desktop/ui/public/locales/es/common.json +++ b/desktop/ui/public/locales/es/common.json @@ -61,7 +61,11 @@ "overwriteRemote": "Sobrescribir remoto", "saveAsNew": "Guardar como nuevo", "keepAllRemote": "Conservar todas las remotas", - "overwriteAllRemote": "Sobrescribir todas las remotas" + "overwriteAllRemote": "Sobrescribir todas las remotas", + "justNow": "Ahora mismo", + "minutesAgo": "hace {{count}} min", + "hoursAgo": "hace {{count}} h", + "daysAgo": "hace {{count}} d" }, "settings": { "title": "Configuración", @@ -138,26 +142,40 @@ "yearsAgo": "Hace {{value}} años" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", - "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "title": "Enlace para compartir de {{name}}", + "linkReady": "Tu enlace para compartir está listo:", + "permission": "Permisos de uso compartido", + "permViewDownload": "Ver + Descargar", + "permPreviewOnly": "Solo vista previa (sin descarga)", + "permAllowEdit": "Ver + Descargar + Editar", + "permUploadOnly": "Solo subir (buzón)", + "expires": "Caduca", + "expire_never": "Nunca caduca", + "expire_oneHour": "1 hora", + "expire_oneDay": "1 día", + "expire_sevenDays": "7 días", + "expire_thirtyDays": "30 días", + "passwordProtect": "Proteger con contraseña", + "password": "Contraseña", + "downloadLimit": "Límite de descargas", + "unlimited": "Ilimitado", + "create": "Crear enlace", + "cancel": "Cancelar", + "done": "Hecho", + "copied": "Enlace copiado al portapapeles" + }, + "update": { + "title": "Actualización de software", + "available": "La versión {{version}} está disponible (tienes {{current}}).", + "downloading": "Descargando actualización…", + "ready": "Actualización instalada — reinicia para aplicar.", + "upToDate": "Estás al día.", + "failed": "Error al comprobar actualizaciones.", + "updateNow": "Actualizar ahora", + "later": "Más tarde", + "restart": "Reiniciar ahora", + "close": "Cerrar", + "checkNow": "Buscar actualizaciones", + "downloadManually": "Descargar manualmente" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/fr/common.json b/desktop/ui/public/locales/fr/common.json index f01211e3..0f45205f 100644 --- a/desktop/ui/public/locales/fr/common.json +++ b/desktop/ui/public/locales/fr/common.json @@ -61,7 +61,11 @@ "overwriteRemote": "Écraser la version distante", "saveAsNew": "Enregistrer comme nouveau fichier", "keepAllRemote": "Conserver toutes les versions distantes", - "overwriteAllRemote": "Écraser toutes les versions distantes" + "overwriteAllRemote": "Écraser toutes les versions distantes", + "justNow": "À l'instant", + "minutesAgo": "il y a {{count}} min", + "hoursAgo": "il y a {{count}} h", + "daysAgo": "il y a {{count}} j" }, "settings": { "title": "Paramètres", @@ -138,26 +142,40 @@ "yearsAgo": "Il y a {{value}} ans" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", - "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "title": "Lien de partage pour {{name}}", + "linkReady": "Votre lien de partage est prêt :", + "permission": "Permissions de partage", + "permViewDownload": "Voir + Télécharger", + "permPreviewOnly": "Aperçu uniquement (sans téléchargement)", + "permAllowEdit": "Voir + Télécharger + Modifier", + "permUploadOnly": "Téléversement uniquement (boîte de dépôt)", + "expires": "Expire", + "expire_never": "N'expire jamais", + "expire_oneHour": "1 heure", + "expire_oneDay": "1 jour", + "expire_sevenDays": "7 jours", + "expire_thirtyDays": "30 jours", + "passwordProtect": "Protéger par mot de passe", + "password": "Mot de passe", + "downloadLimit": "Limite de téléchargements", + "unlimited": "Illimité", + "create": "Créer le partage", + "cancel": "Annuler", + "done": "Terminé", + "copied": "Lien copié dans le presse-papiers" + }, + "update": { + "title": "Mise à jour logicielle", + "available": "La version {{version}} est disponible (vous avez {{current}}).", + "downloading": "Téléchargement de la mise à jour…", + "ready": "Mise à jour installée — redémarrez pour l'appliquer.", + "upToDate": "Vous êtes à jour.", + "failed": "Échec de la vérification des mises à jour.", + "updateNow": "Mettre à jour", + "later": "Plus tard", + "restart": "Redémarrer", + "close": "Fermer", + "checkNow": "Rechercher des mises à jour", + "downloadManually": "Télécharger manuellement" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/it/common.json b/desktop/ui/public/locales/it/common.json index d0c42cc6..47d2b06e 100644 --- a/desktop/ui/public/locales/it/common.json +++ b/desktop/ui/public/locales/it/common.json @@ -61,7 +61,11 @@ "overwriteRemote": "Sovrascrivi remoto", "saveAsNew": "Salva come nuovo", "keepAllRemote": "Mantieni tutte le versioni remote", - "overwriteAllRemote": "Sovrascrivi tutte le versioni remote" + "overwriteAllRemote": "Sovrascrivi tutte le versioni remote", + "justNow": "Proprio ora", + "minutesAgo": "{{count}} min fa", + "hoursAgo": "{{count}} h fa", + "daysAgo": "{{count}} g fa" }, "settings": { "title": "Impostazioni", @@ -138,26 +142,40 @@ "yearsAgo": "{{value}} anni fa" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", + "title": "Link di condivisione per {{name}}", + "linkReady": "Il tuo link di condivisione è pronto:", + "permission": "Permessi di condivisione", + "permViewDownload": "Visualizza + Scarica", + "permPreviewOnly": "Solo anteprima (senza download)", + "permAllowEdit": "Visualizza + Scarica + Modifica", + "permUploadOnly": "Solo caricamento (casella di deposito)", + "expires": "Scade", + "expire_never": "Non scade mai", + "expire_oneHour": "1 ora", + "expire_oneDay": "1 giorno", + "expire_sevenDays": "7 giorni", + "expire_thirtyDays": "30 giorni", + "passwordProtect": "Proteggi con password", "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "downloadLimit": "Limite di download", + "unlimited": "Illimitato", + "create": "Crea condivisione", + "cancel": "Annulla", + "done": "Fatto", + "copied": "Link copiato negli appunti" + }, + "update": { + "title": "Aggiornamento software", + "available": "La versione {{version}} è disponibile (hai la {{current}}).", + "downloading": "Download dell'aggiornamento…", + "ready": "Aggiornamento installato — riavvia per applicare.", + "upToDate": "Sei aggiornato.", + "failed": "Controllo aggiornamenti fallito.", + "updateNow": "Aggiorna ora", + "later": "Più tardi", + "restart": "Riavvia ora", + "close": "Chiudi", + "checkNow": "Controlla aggiornamenti", + "downloadManually": "Scarica manualmente" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/ja/common.json b/desktop/ui/public/locales/ja/common.json index 6acd23bb..e843e81d 100644 --- a/desktop/ui/public/locales/ja/common.json +++ b/desktop/ui/public/locales/ja/common.json @@ -54,14 +54,18 @@ "processing": "処理中...", "conflicts": "競合", "conflictStatus": "{{count}} 件の競合に対応が必要です", - "conflictDescription": "ローカルファイルがリモート版と競合しています", + "conflictDescription": "ローカルファイルがリモートバージョンと競合しています", "resolveConflict": "競合を解決", "resolving": "解決中...", "keepRemote": "リモート版を保持", "overwriteRemote": "リモート版を上書き", "saveAsNew": "新しいファイルとして保存", "keepAllRemote": "すべてリモートを保持", - "overwriteAllRemote": "すべてリモートで上書き" + "overwriteAllRemote": "すべてリモートで上書き", + "justNow": "たった今", + "minutesAgo": "{{count}}分前", + "hoursAgo": "{{count}}時間前", + "daysAgo": "{{count}}日前" }, "settings": { "title": "設定", @@ -138,26 +142,40 @@ "yearsAgo": "{{value}} 年前" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", - "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "title": "{{name}} の共有リンク", + "linkReady": "共有リンクの準備ができました:", + "permission": "共有権限", + "permViewDownload": "表示 + ダウンロード", + "permPreviewOnly": "プレビューのみ (ダウンロード不可)", + "permAllowEdit": "表示 + ダウンロード + 編集", + "permUploadOnly": "アップロードのみ (ドロップボックス)", + "expires": "有効期限", + "expire_never": "無期限", + "expire_oneHour": "1 時間", + "expire_oneDay": "1 日", + "expire_sevenDays": "7 日", + "expire_thirtyDays": "30 日", + "passwordProtect": "パスワードで保護", + "password": "パスワード", + "downloadLimit": "ダウンロード上限", + "unlimited": "無制限", + "create": "共有を作成", + "cancel": "キャンセル", + "done": "完了", + "copied": "リンクをクリップボードにコピーしました" + }, + "update": { + "title": "ソフトウェア更新", + "available": "バージョン {{version}} が利用可能です(現在 {{current}})。", + "downloading": "更新をダウンロード中…", + "ready": "更新をインストールしました — 再起動して適用します。", + "upToDate": "最新の状態です。", + "failed": "更新の確認に失敗しました。", + "updateNow": "今すぐ更新", + "later": "後で", + "restart": "今すぐ再起動", + "close": "閉じる", + "checkNow": "更新を確認", + "downloadManually": "手動でダウンロード" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/ko/common.json b/desktop/ui/public/locales/ko/common.json index 705f90d0..4a76b34f 100644 --- a/desktop/ui/public/locales/ko/common.json +++ b/desktop/ui/public/locales/ko/common.json @@ -61,7 +61,11 @@ "overwriteRemote": "원격 버전 덮어쓰기", "saveAsNew": "새 파일로 저장", "keepAllRemote": "모두 원격 버전 유지", - "overwriteAllRemote": "모두 원격 버전으로 덮어쓰기" + "overwriteAllRemote": "모두 원격 버전으로 덮어쓰기", + "justNow": "방금", + "minutesAgo": "{{count}}분 전", + "hoursAgo": "{{count}}시간 전", + "daysAgo": "{{count}}일 전" }, "settings": { "title": "설정", @@ -138,26 +142,40 @@ "yearsAgo": "{{value}}년 전" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", - "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "title": "{{name}} 공유 링크", + "linkReady": "공유 링크가 준비되었습니다:", + "permission": "공유 권한", + "permViewDownload": "보기 + 다운로드", + "permPreviewOnly": "미리보기 전용 (다운로드 불가)", + "permAllowEdit": "보기 + 다운로드 + 편집", + "permUploadOnly": "업로드 전용 (드롭 박스)", + "expires": "만료", + "expire_never": "만료 없음", + "expire_oneHour": "1시간", + "expire_oneDay": "1일", + "expire_sevenDays": "7일", + "expire_thirtyDays": "30일", + "passwordProtect": "비밀번호로 보호", + "password": "비밀번호", + "downloadLimit": "다운로드 제한", + "unlimited": "제한 없음", + "create": "공유 만들기", + "cancel": "취소", + "done": "완료", + "copied": "링크가 클립보드에 복사되었습니다" + }, + "update": { + "title": "소프트웨어 업데이트", + "available": "버전 {{version}}을(를) 사용할 수 있습니다 (현재 {{current}}).", + "downloading": "업데이트 다운로드 중…", + "ready": "업데이트가 설치되었습니다 — 재시작하여 적용하세요.", + "upToDate": "최신 상태입니다.", + "failed": "업데이트 확인에 실패했습니다.", + "updateNow": "지금 업데이트", + "later": "나중에", + "restart": "지금 재시작", + "close": "닫기", + "checkNow": "업데이트 확인", + "downloadManually": "수동으로 다운로드" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/pl/common.json b/desktop/ui/public/locales/pl/common.json index f5f01edd..f5f4a886 100644 --- a/desktop/ui/public/locales/pl/common.json +++ b/desktop/ui/public/locales/pl/common.json @@ -54,14 +54,18 @@ "processing": "Przetwarzanie...", "conflicts": "Konflikty", "conflictStatus": "{{count}} konflikt(y) wymagają uwagi", - "conflictDescription": "Plik lokalny jest w konflikcie z wersją zdalną", + "conflictDescription": "Lokalny plik jest w konflikcie z wersją zdalną", "resolveConflict": "Rozwiąż konflikt", "resolving": "Rozwiązywanie...", "keepRemote": "Zachowaj wersję zdalną", "overwriteRemote": "Nadpisz wersję zdalną", "saveAsNew": "Zapisz jako nowy plik", "keepAllRemote": "Zachowaj wszystkie zdalne", - "overwriteAllRemote": "Nadpisz wszystkie zdalne" + "overwriteAllRemote": "Nadpisz wszystkie zdalne", + "justNow": "Przed chwilą", + "minutesAgo": "{{count}} min temu", + "hoursAgo": "{{count}} godz. temu", + "daysAgo": "{{count}} dni temu" }, "settings": { "title": "Ustawienia", @@ -138,26 +142,40 @@ "yearsAgo": "{{value}} lat temu" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", - "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "title": "Link do udostępnienia dla {{name}}", + "linkReady": "Twój link do udostępnienia jest gotowy:", + "permission": "Uprawnienia udostępniania", + "permViewDownload": "Podgląd + Pobieranie", + "permPreviewOnly": "Tylko podgląd (bez pobierania)", + "permAllowEdit": "Podgląd + Pobieranie + Edycja", + "permUploadOnly": "Tylko przesyłanie (skrzynka nadawcza)", + "expires": "Wygasa", + "expire_never": "Nigdy nie wygasa", + "expire_oneHour": "1 godzina", + "expire_oneDay": "1 dzień", + "expire_sevenDays": "7 dni", + "expire_thirtyDays": "30 dni", + "passwordProtect": "Chroń hasłem", + "password": "Hasło", + "downloadLimit": "Limit pobrań", + "unlimited": "Bez limitu", + "create": "Utwórz udostępnienie", + "cancel": "Anuluj", + "done": "Gotowe", + "copied": "Link skopiowany do schowka" + }, + "update": { + "title": "Aktualizacja oprogramowania", + "available": "Dostępna jest wersja {{version}} (masz {{current}}).", + "downloading": "Pobieranie aktualizacji…", + "ready": "Aktualizacja zainstalowana — uruchom ponownie, aby zastosować.", + "upToDate": "Masz najnowszą wersję.", + "failed": "Sprawdzanie aktualizacji nie powiodło się.", + "updateNow": "Aktualizuj teraz", + "later": "Później", + "restart": "Uruchom ponownie", + "close": "Zamknij", + "checkNow": "Sprawdź aktualizacje", + "downloadManually": "Pobierz ręcznie" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/ru/common.json b/desktop/ui/public/locales/ru/common.json index 8bd40ab1..65493955 100644 --- a/desktop/ui/public/locales/ru/common.json +++ b/desktop/ui/public/locales/ru/common.json @@ -61,7 +61,11 @@ "overwriteRemote": "Перезаписать удалённую версию", "saveAsNew": "Сохранить как новый файл", "keepAllRemote": "Оставить все удалённые", - "overwriteAllRemote": "Перезаписать все удалённые" + "overwriteAllRemote": "Перезаписать все удалённые", + "justNow": "Только что", + "minutesAgo": "{{count}} мин назад", + "hoursAgo": "{{count}} ч назад", + "daysAgo": "{{count}} д назад" }, "settings": { "title": "Настройки", @@ -138,26 +142,40 @@ "yearsAgo": "{{value}} лет назад" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", - "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "title": "Ссылка на {{name}}", + "linkReady": "Ваша ссылка готова:", + "permission": "Права доступа", + "permViewDownload": "Просмотр + Скачивание", + "permPreviewOnly": "Только просмотр (без скачивания)", + "permAllowEdit": "Просмотр + Скачивание + Редактирование", + "permUploadOnly": "Только загрузка (ящик для файлов)", + "expires": "Истекает", + "expire_never": "Без срока", + "expire_oneHour": "1 час", + "expire_oneDay": "1 день", + "expire_sevenDays": "7 дней", + "expire_thirtyDays": "30 дней", + "passwordProtect": "Защитить паролем", + "password": "Пароль", + "downloadLimit": "Лимит скачиваний", + "unlimited": "Без ограничений", + "create": "Создать ссылку", + "cancel": "Отмена", + "done": "Готово", + "copied": "Ссылка скопирована в буфер обмена" + }, + "update": { + "title": "Обновление", + "available": "Доступна версия {{version}} (у вас {{current}}).", + "downloading": "Загрузка обновления…", + "ready": "Обновление установлено — перезапустите для применения.", + "upToDate": "У вас последняя версия.", + "failed": "Не удалось проверить обновления.", + "updateNow": "Обновить", + "later": "Позже", + "restart": "Перезапустить", + "close": "Закрыть", + "checkNow": "Проверить обновления", + "downloadManually": "Скачать вручную" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/zh-CN/common.json b/desktop/ui/public/locales/zh-CN/common.json index 9c7e75e9..dbf0431a 100644 --- a/desktop/ui/public/locales/zh-CN/common.json +++ b/desktop/ui/public/locales/zh-CN/common.json @@ -61,7 +61,11 @@ "overwriteRemote": "覆盖远程版本", "saveAsNew": "另存为新文件", "keepAllRemote": "全部保留远程", - "overwriteAllRemote": "全部覆盖远程" + "overwriteAllRemote": "全部覆盖远程", + "justNow": "刚刚", + "minutesAgo": "{{count}} 分钟前", + "hoursAgo": "{{count}} 小时前", + "daysAgo": "{{count}} 天前" }, "settings": { "title": "设置", @@ -159,5 +163,19 @@ "cancel": "取消", "done": "完成", "copied": "链接已复制到剪贴板" + }, + "update": { + "title": "软件更新", + "available": "新版本 {{version}} 可用(当前版本 {{current}})。", + "downloading": "正在下载更新…", + "ready": "更新已安装 — 重启以应用。", + "upToDate": "已是最新版本。", + "failed": "更新检查失败。", + "updateNow": "立即更新", + "later": "稍后", + "restart": "立即重启", + "close": "关闭", + "checkNow": "检查更新", + "downloadManually": "手动下载" } -} +} \ No newline at end of file diff --git a/desktop/ui/public/locales/zh-TW/common.json b/desktop/ui/public/locales/zh-TW/common.json index fdd48619..32e40425 100644 --- a/desktop/ui/public/locales/zh-TW/common.json +++ b/desktop/ui/public/locales/zh-TW/common.json @@ -61,7 +61,11 @@ "overwriteRemote": "覆蓋遠端版本", "saveAsNew": "另存為新檔案", "keepAllRemote": "全部保留遠端", - "overwriteAllRemote": "全部覆寫遠端" + "overwriteAllRemote": "全部覆寫遠端", + "justNow": "剛剛", + "minutesAgo": "{{count}} 分鐘前", + "hoursAgo": "{{count}} 小時前", + "daysAgo": "{{count}} 天前" }, "settings": { "title": "設定", @@ -138,26 +142,40 @@ "yearsAgo": "{{value}} 年前" }, "share": { - "title": "Share link for {{name}}", - "linkReady": "Your share link is ready:", - "permission": "Sharing permissions", - "permViewDownload": "View + Download", - "permPreviewOnly": "Preview only (no download)", - "permAllowEdit": "View + Download + Edit", - "permUploadOnly": "Upload only (drop box)", - "expires": "Expires", - "expire_never": "Never expires", - "expire_oneHour": "1 hour", - "expire_oneDay": "1 day", - "expire_sevenDays": "7 days", - "expire_thirtyDays": "30 days", - "passwordProtect": "Protect with password", - "password": "Password", - "downloadLimit": "Download limit", - "unlimited": "Unlimited", - "create": "Create share", - "cancel": "Cancel", - "done": "Done", - "copied": "Link copied to clipboard" + "title": "{{name}} 的分享連結", + "linkReady": "您的分享連結已就緒:", + "permission": "分享權限", + "permViewDownload": "檢視 + 下載", + "permPreviewOnly": "僅預覽 (不可下載)", + "permAllowEdit": "檢視 + 下載 + 編輯", + "permUploadOnly": "僅上傳 (收集箱)", + "expires": "到期", + "expire_never": "永不過期", + "expire_oneHour": "1 小時", + "expire_oneDay": "1 天", + "expire_sevenDays": "7 天", + "expire_thirtyDays": "30 天", + "passwordProtect": "使用密碼保護", + "password": "密碼", + "downloadLimit": "下載次數限制", + "unlimited": "無限制", + "create": "建立分享", + "cancel": "取消", + "done": "完成", + "copied": "連結已複製到剪貼簿" + }, + "update": { + "title": "軟體更新", + "available": "新版本 {{version}} 可用(目前版本 {{current}})。", + "downloading": "正在下載更新…", + "ready": "更新已安裝 — 重新啟動以套用。", + "upToDate": "已是最新版本。", + "failed": "更新檢查失敗。", + "updateNow": "立即更新", + "later": "稍後", + "restart": "立即重啟", + "close": "關閉", + "checkNow": "檢查更新", + "downloadManually": "手動下載" } -} +} \ No newline at end of file diff --git a/desktop/ui/src/App.tsx b/desktop/ui/src/App.tsx index b0ff647c..cf2eb6ae 100644 --- a/desktop/ui/src/App.tsx +++ b/desktop/ui/src/App.tsx @@ -6,6 +6,7 @@ import { useMediaQuery, } from "@mui/material"; import { Routes, Route, HashRouter } from "react-router-dom"; +import Update from "./pages/Update"; import "@fontsource/roboto/300.css"; import "@fontsource/roboto/400.css"; import "@fontsource/roboto/500.css"; @@ -49,6 +50,7 @@ function App() { } /> } /> } /> + } /> diff --git a/desktop/ui/src/pages/Update.tsx b/desktop/ui/src/pages/Update.tsx new file mode 100644 index 00000000..76e8e4c9 --- /dev/null +++ b/desktop/ui/src/pages/Update.tsx @@ -0,0 +1,227 @@ +import { + Alert, + Box, + Button, + CircularProgress, + IconButton, + LinearProgress, + Typography, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { invoke } from "@tauri-apps/api/core"; +import { listen } from "@tauri-apps/api/event"; +import { getCurrentWindow } from "@tauri-apps/api/window"; +import { openUrl } from "@tauri-apps/plugin-opener"; +import { useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { DenseFilledTextField } from "../common/StyledComponent"; + +type UpdateInfo = { + version: string; + current_version: string; + notes?: string | null; + date?: string | null; +}; + +type Phase = "checking" | "available" | "downloading" | "ready" | "uptodate" | "error"; + +const RELEASES_URL = "https://github.com/Dvorinka/cloudreve/releases"; + +export default function Update() { + const { t } = useTranslation(); + const [phase, setPhase] = useState("checking"); + const [info, setInfo] = useState(null); + const [progress, setProgress] = useState(0); + const [error, setError] = useState(null); + const downloadedRef = useRef(0); + + useEffect(() => { + const unProgress = listen<{ downloaded: number; total: number | null }>( + "update-progress", + (e) => { + downloadedRef.current += e.payload.downloaded; + if (e.payload.total) { + setProgress( + Math.min(100, (downloadedRef.current / e.payload.total) * 100) + ); + } + } + ); + const unFinished = listen("update-finished", () => setPhase("ready")); + + invoke("check_update") + .then((u) => { + setInfo(u); + setPhase(u ? "available" : "uptodate"); + }) + .catch((e) => { + setError(String(e)); + setPhase("error"); + }); + + return () => { + unProgress.then((f) => f()); + unFinished.then((f) => f()); + }; + }, []); + + const startUpdate = async () => { + setPhase("downloading"); + setError(null); + downloadedRef.current = 0; + setProgress(0); + try { + await invoke("install_update"); + // The ready phase is set by the update-finished listener; if the + // event raced past us, still land on ready. + setPhase((p) => (p === "downloading" ? "ready" : p)); + } catch (e) { + setError(String(e)); + setPhase("error"); + } + }; + + return ( + + + + {t("update.title")} + + getCurrentWindow().close()} + sx={{ WebkitAppRegion: "no-drag", appRegion: "no-drag" }} + > + + + + + + {phase === "checking" && ( + + + + )} + + {phase === "available" && info && ( + <> + + {t("update.available", { + version: info.version, + current: info.current_version, + })} + + {info.notes && ( + + )} + + )} + + {phase === "downloading" && ( + <> + + {t("update.downloading")} + + + + {Math.round(progress)}% + + + )} + + {phase === "ready" && ( + {t("update.ready")} + )} + + {phase === "uptodate" && ( + {t("update.upToDate")} + )} + + {phase === "error" && ( + {error ?? t("update.failed")} + )} + + + + {phase === "available" && ( + <> + + + + )} + {phase === "ready" && ( + + )} + {(phase === "uptodate" || phase === "checking") && ( + + )} + {phase === "error" && ( + <> + + + + )} + + + ); +} diff --git a/desktop/ui/src/pages/popup/utils.ts b/desktop/ui/src/pages/popup/utils.ts index 99b4b1cc..201d0fbc 100644 --- a/desktop/ui/src/pages/popup/utils.ts +++ b/desktop/ui/src/pages/popup/utils.ts @@ -1,3 +1,5 @@ +import i18n from "../../i18n"; + export function formatBytes(bytes: number): string { if (bytes === 0) return "0 B"; const k = 1024; @@ -10,10 +12,12 @@ export function formatRelativeTime(timestamp: number): string { const now = Date.now() / 1000; const diff = now - timestamp; - if (diff < 60) return "Just now"; - if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; - if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; - return `${Math.floor(diff / 86400)}d ago`; + if (diff < 60) return i18n.t("popup.justNow"); + if (diff < 3600) + return i18n.t("popup.minutesAgo", { count: Math.floor(diff / 60) }); + if (diff < 86400) + return i18n.t("popup.hoursAgo", { count: Math.floor(diff / 3600) }); + return i18n.t("popup.daysAgo", { count: Math.floor(diff / 86400) }); } export function getFileName(path: string): string { diff --git a/desktop/ui/src/pages/settings/AboutSection.tsx b/desktop/ui/src/pages/settings/AboutSection.tsx index 3dced002..4297a8b9 100644 --- a/desktop/ui/src/pages/settings/AboutSection.tsx +++ b/desktop/ui/src/pages/settings/AboutSection.tsx @@ -1,10 +1,12 @@ import { useEffect, useState } from "react"; -import { Box, Typography, Link, Chip, Stack } from "@mui/material"; +import { Box, Typography, Link, Chip, Stack, Button } from "@mui/material"; import { useTranslation } from "react-i18next"; import { getVersion } from "@tauri-apps/api/app"; +import { invoke } from "@tauri-apps/api/core"; import GitHubIcon from "@mui/icons-material/GitHub"; import BugReportIcon from "@mui/icons-material/BugReportRounded"; import ForumIcon from "@mui/icons-material/ForumRounded"; +import SystemUpdateAltIcon from "@mui/icons-material/SystemUpdateAlt"; import logo from "../../assets/cloudreve.svg"; import { HomeRounded } from "@mui/icons-material"; @@ -33,12 +35,12 @@ export default function AboutSection() { { icon: , label: "GitHub", - href: "https://github.com/cloudreve/desktop", + href: "https://github.com/Dvorinka/cloudreve", }, { icon: , label: t("about.reportIssue"), - href: "https://github.com/cloudreve/desktop/issues", + href: "https://github.com/Dvorinka/cloudreve/issues", }, { icon: , @@ -79,6 +81,16 @@ export default function AboutSection() { + + {links.map((link) => ( http://127.0.0.1:8080/.", "tempPath": "مجلد التنزيل المؤقت", "tempPathDes": "المجلد على العقدة الذي يستخدمه Aria2 كمجلد تنزيل مؤقت. تحتاج عملية Cloudreve على العقدة إلى أذونات القراءة والكتابة والتنفيذ على هذا المجلد، ويجب أن يتمكن برنامج التنزيل أيضًا من الوصول إليه. اتركه فارغًا لاستخدام مسار الملفات المؤقتة الافتراضي.", @@ -1422,7 +1421,8 @@ "otherConditions": "شروط أخرى", "shareLinkExisted": "لديه رابط مشاركة", "directLinkExisted": "لديه رابط مباشر", - "isUploading": "قيد التحميل" + "isUploading": "قيد التحميل", + "selectUser": "اختر مستخدمًا" }, "entity": { "refenenceCount": "عدد المراجع", @@ -1758,4 +1758,4 @@ "shareID": "معرف المشاركة", "reason": "السبب" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/de-DE/application.json b/frontend/public/locales/de-DE/application.json index 83c5af65..bdf72dde 100644 --- a/frontend/public/locales/de-DE/application.json +++ b/frontend/public/locales/de-DE/application.json @@ -529,7 +529,7 @@ "everyone": "Alle anderen", "otherGroup": "Andere Benutzergruppen", "sameGroup": "Dieselbe Benutzergruppe wie ich", - "anonymous": "Anonyme Besucher", + "anonymous": "Anonym", "noResults": "Keine Ergebnisse", "searchGroupUser": "Benutzer oder Benutzergruppen suchen...", "resetToDefault": "Auf Standard zurücksetzen", @@ -664,7 +664,8 @@ "viewSetting": "Ansichtseinstellungen", "saved": "Gespeichert", "notSet": "Nicht gesetzt", - "deleteViewSetting": "Ansichtseinstellung löschen" + "deleteViewSetting": "Ansichtseinstellung löschen", + "shareTargetsCount": "{{count}} Ziele" }, "uploader": { "fileCopyName": "Kopie_", @@ -1108,7 +1109,12 @@ "report": "Missbrauch melden", "reportTarget": "Meldeobjekt", "reportReason": "Grund", - "reportReasonOptions": ["Urheberrechtsverletzung", "Schädlicher Inhalt", "Spam", "Andere"], + "reportReasonOptions": [ + "Urheberrechtsverletzung", + "Schädlicher Inhalt", + "Spam", + "Andere" + ], "reportDescription": "Zusätzliche Beschreibung", "reportAbuseSuccess": "Meldung eingereicht", "migrateStoragePolicy": "Speicherrichtlinie übertragen", @@ -1187,4 +1193,4 @@ "openPaymentLink": "Zahlungslink direkt öffnen", "creditReasonAdjust": "Manuelle Anpassung" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/de-DE/common.json b/frontend/public/locales/de-DE/common.json index 98d2275b..776d9309 100644 --- a/frontend/public/locales/de-DE/common.json +++ b/frontend/public/locales/de-DE/common.json @@ -116,5 +116,6 @@ "40072": "Sie sind Administrator und können keine andere Gruppe erwerben.", "40084": "Ihr Konto ist passwortlos. Sie müssen mindestens ein verknüpftes Konto behalten.", "40085": "Der Gesamtbetrag dieser Bestellung ist zu gering für den Checkout." - } -} + }, + "save": "Speichern" +} \ No newline at end of file diff --git a/frontend/public/locales/de-DE/dashboard.json b/frontend/public/locales/de-DE/dashboard.json index ba162936..00447053 100644 --- a/frontend/public/locales/de-DE/dashboard.json +++ b/frontend/public/locales/de-DE/dashboard.json @@ -1428,7 +1428,8 @@ "otherConditions": "Andere Bedingungen", "shareLinkExisted": "Hat Freigabe-Link", "directLinkExisted": "Hat direkten Link", - "isUploading": "Wird hochgeladen" + "isUploading": "Wird hochgeladen", + "selectUser": "Benutzer auswählen" }, "entity": { "refenenceCount": "Referenzzähler", @@ -1474,7 +1475,8 @@ "rawContent": "Roher Inhalt", "confirmDelete": "Sind Sie sicher, dass Sie dieses Ereignis löschen möchten?", "deleteXEvents": "{{num}} Ereignisse löschen", - "confirmBatchDelete": "Sind Sie sicher, dass Sie {{num}} Ereignisse löschen möchten?" + "confirmBatchDelete": "Sind Sie sicher, dass Sie {{num}} Ereignisse löschen möchten?", + "allEventTypes": "Alle Ereignistypen" }, "share": { "confirmBatchDelete": "Sind Sie sicher, dass Sie {{num}} Freigaben löschen möchten?", @@ -1747,4 +1749,4 @@ "shareID": "Freigabe-ID", "reason": "Grund" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/en-US/application.json b/frontend/public/locales/en-US/application.json index 58539f2e..871387cf 100644 --- a/frontend/public/locales/en-US/application.json +++ b/frontend/public/locales/en-US/application.json @@ -698,12 +698,13 @@ "notSet": "Not set", "deleteViewSetting": "Delete view setting", "shareTargets": "Share targets", - "shareTargetsCount_one": "{{count}} file selected", - "shareTargetsCount_other": "{{count}} files selected", + "shareTargetsCount_one": "{{count}} target", + "shareTargetsCount_other": "{{count}} targets", "publicListing": "List in public directory", "publicListingDes": "Show this share on the Discover page, visible to anyone including anonymous visitors. Not available for password-protected shares.", "hideReadme": "Hide readme file", - "hideReadmeDes": "Keep the README file itself out of the share listing. Visitors still see the rendered readme." + "hideReadmeDes": "Keep the README file itself out of the share listing. Visitors still see the rendered readme.", + "anonymous": "Anonymous" }, "uploader": { "fileCopyName": "Copy of ", @@ -1218,4 +1219,4 @@ "discover": { "searchPlaceholder": "Search public shares" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/en-US/common.json b/frontend/public/locales/en-US/common.json index 3df7a55d..59371768 100644 --- a/frontend/public/locales/en-US/common.json +++ b/frontend/public/locales/en-US/common.json @@ -105,5 +105,6 @@ "50010": "Desired node is unavailable.", "50011": "Failed to query file metadata." }, - "delete": "Delete" -} + "delete": "Delete", + "save": "Save" +} \ No newline at end of file diff --git a/frontend/public/locales/en-US/dashboard.json b/frontend/public/locales/en-US/dashboard.json index 9e2118c1..a2ea1e6e 100644 --- a/frontend/public/locales/en-US/dashboard.json +++ b/frontend/public/locales/en-US/dashboard.json @@ -1611,7 +1611,8 @@ "isUploading": "Is uploading", "deletedState": "Deleted state", "liveOnly": "Live files only", - "trashOnly": "Trash only" + "trashOnly": "Trash only", + "selectUser": "Select user" }, "entity": { "refenenceCount": "Reference count", diff --git a/frontend/public/locales/es-ES/application.json b/frontend/public/locales/es-ES/application.json index c7f94d89..d8475668 100644 --- a/frontend/public/locales/es-ES/application.json +++ b/frontend/public/locales/es-ES/application.json @@ -529,7 +529,7 @@ "everyone": "Todos los demás", "otherGroup": "Otros grupos", "sameGroup": "Mismo grupo que yo", - "anonymous": "Visitantes anónimos", + "anonymous": "Anónimo", "noResults": "Sin resultados", "searchGroupUser": "Buscar emails o grupos...", "resetToDefault": "Restablecer a predeterminado", @@ -660,7 +660,8 @@ "skipSoftDelete": "Eliminar permanentemente", "skipSoftDeleteDes": "Omitir mover a papelera, eliminar permanentemente", "unlinkOnly": "Mantener archivos físicos", - "unlinkOnlyDes": "Eliminar solo registros de archivos, los archivos físicos no serán eliminados." + "unlinkOnlyDes": "Eliminar solo registros de archivos, los archivos físicos no serán eliminados.", + "shareTargetsCount": "{{count}} destinos" }, "uploader": { "fileCopyName": "Copia de ", @@ -1106,7 +1107,12 @@ "report": "Reportar abuso", "reportTarget": "Objeto del reporte", "reportReason": "Razón", - "reportReasonOptions": ["Infracción de derechos de autor", "Contenido dañino", "Información basura", "Otro"], + "reportReasonOptions": [ + "Infracción de derechos de autor", + "Contenido dañino", + "Información basura", + "Otro" + ], "reportDescription": "Descripción adicional", "reportAbuseSuccess": "Reporte enviado", "migrateStoragePolicy": "Transferir política de almacenamiento", @@ -1185,4 +1191,4 @@ "openPaymentLink": "Abrir enlace de pago directamente", "creditReasonAdjust": "Ajuste manual" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/es-ES/common.json b/frontend/public/locales/es-ES/common.json index 6013d3eb..73e3ff67 100644 --- a/frontend/public/locales/es-ES/common.json +++ b/frontend/public/locales/es-ES/common.json @@ -116,5 +116,6 @@ "40072": "Eres administrador, no puedes comprar otro grupo.", "40084": "Tu cuenta no tiene contraseña, debes mantener al menos una cuenta vinculada.", "40085": "El monto total de esta orden es demasiado pequeño para el pago." - } -} + }, + "save": "Guardar" +} \ No newline at end of file diff --git a/frontend/public/locales/es-ES/dashboard.json b/frontend/public/locales/es-ES/dashboard.json index 9fe60778..43bcbcb2 100644 --- a/frontend/public/locales/es-ES/dashboard.json +++ b/frontend/public/locales/es-ES/dashboard.json @@ -1421,7 +1421,8 @@ "otherConditions": "Otras condiciones", "shareLinkExisted": "Tiene enlace de compartir", "directLinkExisted": "Tiene enlace directo", - "isUploading": "Está subiendo" + "isUploading": "Está subiendo", + "selectUser": "Seleccionar usuario" }, "entity": { "refenenceCount": "Conteo de referencia", @@ -1467,7 +1468,8 @@ "rawContent": "Contenido sin procesar", "confirmDelete": "¿Estás seguro de que quieres eliminar este evento?", "deleteXEvents": "Eliminar {{num}} eventos", - "confirmBatchDelete": "¿Estás seguro de que quieres eliminar {{num}} eventos?" + "confirmBatchDelete": "¿Estás seguro de que quieres eliminar {{num}} eventos?", + "allEventTypes": "Todos los tipos de eventos" }, "share": { "confirmBatchDelete": "¿Estás seguro de que quieres eliminar {{num}} compartidos?", @@ -1756,4 +1758,4 @@ "shareID": "ID", "reason": "Razón" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/fr-FR/application.json b/frontend/public/locales/fr-FR/application.json index fca1a6d6..0756f008 100644 --- a/frontend/public/locales/fr-FR/application.json +++ b/frontend/public/locales/fr-FR/application.json @@ -529,7 +529,7 @@ "everyone": "Tous les autres", "otherGroup": "Autres groupes", "sameGroup": "Même groupe que moi", - "anonymous": "Visiteurs anonymes", + "anonymous": "Anonyme", "noResults": "Aucun résultat", "searchGroupUser": "Rechercher des e-mails ou des groupes...", "resetToDefault": "Réinitialiser par défaut", @@ -664,7 +664,8 @@ "viewSetting": "Paramètre de vue", "saved": "Enregistré", "notSet": "Non défini", - "deleteViewSetting": "Supprimer le paramètre de vue" + "deleteViewSetting": "Supprimer le paramètre de vue", + "shareTargetsCount": "{{count}} cibles" }, "uploader": { "fileCopyName": "Copie de ", @@ -1110,7 +1111,12 @@ "report": "Signaler un abus", "reportTarget": "Cible du signalement", "reportReason": "Raison", - "reportReasonOptions": ["Violation de droits d'auteur", "Contenu nuisible", "Spam", "Autre"], + "reportReasonOptions": [ + "Violation de droits d'auteur", + "Contenu nuisible", + "Spam", + "Autre" + ], "reportDescription": "Description supplémentaire", "reportAbuseSuccess": "Signalement soumis.", "migrateStoragePolicy": "Migrer la stratégie de stockage", @@ -1189,4 +1195,4 @@ "openPaymentLink": "Ouvrir le lien de paiement", "creditReasonAdjust": "Ajustement manuel" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/fr-FR/common.json b/frontend/public/locales/fr-FR/common.json index 1f5faac0..764ed572 100644 --- a/frontend/public/locales/fr-FR/common.json +++ b/frontend/public/locales/fr-FR/common.json @@ -116,5 +116,6 @@ "40072": "Vous êtes administrateur, vous ne pouvez pas acheter d'autre groupe.", "40084": "Votre compte est sans mot de passe, vous devez conserver au moins un compte lié.", "40085": "Le montant total de cette commande est trop petit pour le paiement." - } -} + }, + "save": "Enregistrer" +} \ No newline at end of file diff --git a/frontend/public/locales/fr-FR/dashboard.json b/frontend/public/locales/fr-FR/dashboard.json index 856e1aff..13ad00a3 100644 --- a/frontend/public/locales/fr-FR/dashboard.json +++ b/frontend/public/locales/fr-FR/dashboard.json @@ -1421,7 +1421,8 @@ "otherConditions": "Autres conditions", "shareLinkExisted": "A un lien de partage", "directLinkExisted": "A un lien direct", - "isUploading": "En cours de téléchargement" + "isUploading": "En cours de téléchargement", + "selectUser": "Sélectionner un utilisateur" }, "entity": { "refenenceCount": "Nombre de références", @@ -1467,7 +1468,8 @@ "rawContent": "Contenu brut", "confirmDelete": "Êtes-vous sûr de vouloir supprimer cet événement ?", "deleteXEvents": "Supprimer {{num}} événements", - "confirmBatchDelete": "Êtes-vous sûr de vouloir supprimer {{num}} événements ?" + "confirmBatchDelete": "Êtes-vous sûr de vouloir supprimer {{num}} événements ?", + "allEventTypes": "Tous les types d'événements" }, "share": { "confirmBatchDelete": "Êtes-vous sûr de vouloir supprimer {{num}} partages ?", @@ -1756,4 +1758,4 @@ "shareID": "ID partagé", "reason": "Raison" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/it-IT/application.json b/frontend/public/locales/it-IT/application.json index 835e93a2..d6e6b453 100644 --- a/frontend/public/locales/it-IT/application.json +++ b/frontend/public/locales/it-IT/application.json @@ -529,7 +529,7 @@ "everyone": "Tutti gli altri", "otherGroup": "Altri gruppi", "sameGroup": "Stesso gruppo di me", - "anonymous": "Visitatori anonimi", + "anonymous": "Anonimo", "noResults": "Nessun risultato", "searchGroupUser": "Cerca email o gruppi...", "resetToDefault": "Ripristina predefinito", @@ -660,7 +660,8 @@ "skipSoftDelete": "Elimina permanentemente", "skipSoftDeleteDes": "Salta lo spostamento nel cestino, elimina permanentemente", "unlinkOnly": "Mantieni file fisici", - "unlinkOnlyDes": "Elimina solo i record dei file, i file fisici non saranno eliminati." + "unlinkOnlyDes": "Elimina solo i record dei file, i file fisici non saranno eliminati.", + "shareTargetsCount": "{{count}} destinazioni" }, "uploader": { "fileCopyName": "Copia di ", @@ -1106,7 +1107,12 @@ "report": "Segnala abuso", "reportTarget": "Target segnalazione", "reportReason": "Motivo", - "reportReasonOptions": ["Violazione copyright", "Contenuto dannoso", "Spam", "Altro"], + "reportReasonOptions": [ + "Violazione copyright", + "Contenuto dannoso", + "Spam", + "Altro" + ], "reportDescription": "Descrizione aggiuntiva", "reportAbuseSuccess": "Segnalazione inviata.", "migrateStoragePolicy": "Migra policy di archiviazione", @@ -1183,4 +1189,4 @@ "openPaymentLink": "Apri link di pagamento", "creditReasonAdjust": "Aggiustamento manuale" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/it-IT/common.json b/frontend/public/locales/it-IT/common.json index e564ee7b..7be2ba99 100644 --- a/frontend/public/locales/it-IT/common.json +++ b/frontend/public/locales/it-IT/common.json @@ -116,5 +116,6 @@ "40072": "Sei amministratore, non puoi acquistare altri gruppi.", "40084": "Il tuo account è senza password, devi mantenere almeno un account collegato.", "40085": "L'importo totale di questo ordine è troppo piccolo per il checkout." - } -} + }, + "save": "Salva" +} \ No newline at end of file diff --git a/frontend/public/locales/it-IT/dashboard.json b/frontend/public/locales/it-IT/dashboard.json index 30f3d1e0..7d879ccf 100644 --- a/frontend/public/locales/it-IT/dashboard.json +++ b/frontend/public/locales/it-IT/dashboard.json @@ -1420,7 +1420,8 @@ "otherConditions": "Altre condizioni", "shareLinkExisted": "Ha link di condivisione", "directLinkExisted": "Ha link diretto", - "isUploading": "In caricamento" + "isUploading": "In caricamento", + "selectUser": "Seleziona utente" }, "entity": { "refenenceCount": "Conteggio riferimenti", @@ -1466,7 +1467,8 @@ "rawContent": "Contenuto raw", "confirmDelete": "Sei sicuro di voler eliminare questo evento?", "deleteXEvents": "Elimina {{num}} eventi", - "confirmBatchDelete": "Sei sicuro di voler eliminare {{num}} eventi?" + "confirmBatchDelete": "Sei sicuro di voler eliminare {{num}} eventi?", + "allEventTypes": "Tutti i tipi di evento" }, "share": { "confirmBatchDelete": "Sei sicuro di voler eliminare {{num}} condivisioni?", @@ -1755,4 +1757,4 @@ "shareID": "ID Condiviso", "reason": "Motivo" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/ja-JP/application.json b/frontend/public/locales/ja-JP/application.json index 70decba4..88be3b51 100644 --- a/frontend/public/locales/ja-JP/application.json +++ b/frontend/public/locales/ja-JP/application.json @@ -611,7 +611,9 @@ "viewSetting": "表示設定", "saved": "保存されました", "notSet": "未設定", - "deleteViewSetting": "表示設定の削除" + "deleteViewSetting": "表示設定の削除", + "anonymous": "匿名", + "shareTargetsCount": "{{count}} 件の対象" }, "uploader": { "fileCopyName": "コピー_", @@ -994,8 +996,13 @@ "validDurationDays": "{{num}} 日", "reportTarget": "報告対象", "reportReason": "理由", - "reportReasonOptions": ["著作権侵害", "有害なコンテンツ", "スパム", "その他"], + "reportReasonOptions": [ + "著作権侵害", + "有害なコンテンツ", + "スパム", + "その他" + ], "reportDescription": "詳細説明", "reportAbuseSuccess": "報告を受け付けました" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/ja-JP/common.json b/frontend/public/locales/ja-JP/common.json index 5151444c..fc504a1e 100644 --- a/frontend/public/locales/ja-JP/common.json +++ b/frontend/public/locales/ja-JP/common.json @@ -102,5 +102,6 @@ "50005": "内部エラーが発生しました ({{message}})", "50010": "対象ノードが利用できません", "50011": "ファイルメタデータの取得に失敗しました" - } -} + }, + "save": "保存" +} \ No newline at end of file diff --git a/frontend/public/locales/ja-JP/dashboard.json b/frontend/public/locales/ja-JP/dashboard.json index 7e813701..f7988d87 100644 --- a/frontend/public/locales/ja-JP/dashboard.json +++ b/frontend/public/locales/ja-JP/dashboard.json @@ -1422,7 +1422,8 @@ "otherConditions": "その他の条件", "shareLinkExisted": "共有リンクが存在", "directLinkExisted": "直接リンクが存在", - "isUploading": "アップロード中" + "isUploading": "アップロード中", + "selectUser": "ユーザーを選択" }, "entity": { "refenenceCount": "参照回数", @@ -1442,7 +1443,7 @@ "cleanupNotAfter": "この日付以前", "cleanupEventTypes": "イベント種類", "cleanupEventTypesDes": "イベント種類を選択してください。空の場合はすべての種類を削除します。", - "allEventTypes": "すべてのイベントタイプ", + "allEventTypes": "すべてのイベント種別", "initiator": "発信者", "event": "イベント", "userID": "ユーザーID", @@ -1758,4 +1759,4 @@ "shareID": "共有 ID", "reason": "原因" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/ko-KR/application.json b/frontend/public/locales/ko-KR/application.json index 66ad0936..9acac1a1 100644 --- a/frontend/public/locales/ko-KR/application.json +++ b/frontend/public/locales/ko-KR/application.json @@ -529,7 +529,7 @@ "everyone": "기타 모든 사람", "otherGroup": "다른 사용자 그룹", "sameGroup": "나와 같은 사용자 그룹", - "anonymous": "익명 방문자", + "anonymous": "익명", "noResults": "결과 없음", "searchGroupUser": "사용자 또는 사용자 그룹 검색...", "resetToDefault": "기본값으로 재설정", @@ -664,7 +664,8 @@ "viewSetting": "보기 설정", "saved": "저장됨", "notSet": "설정되지 않음", - "deleteViewSetting": "보기 설정 삭제" + "deleteViewSetting": "보기 설정 삭제", + "shareTargetsCount": "대상 {{count}}개" }, "uploader": { "fileCopyName": "사본_", @@ -1110,7 +1111,12 @@ "report": "남용 신고", "reportTarget": "신고 대상", "reportReason": "이유", - "reportReasonOptions": ["침해", "유해 콘텐츠", "스팸 정보", "기타"], + "reportReasonOptions": [ + "침해", + "유해 콘텐츠", + "스팸 정보", + "기타" + ], "reportDescription": "추가 설명", "reportAbuseSuccess": "신고가 제출되었습니다", "migrateStoragePolicy": "저장 정책 전송", @@ -1189,4 +1195,4 @@ "openPaymentLink": "결제 링크 직접 열기", "creditReasonAdjust": "수동 조정" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/ko-KR/common.json b/frontend/public/locales/ko-KR/common.json index 98a1c399..a72b7218 100644 --- a/frontend/public/locales/ko-KR/common.json +++ b/frontend/public/locales/ko-KR/common.json @@ -116,5 +116,6 @@ "40072": "관리자는 다른 사용자 그룹으로 업그레이드할 수 없습니다", "40084": "귀하의 계정은 비밀번호가 없는 계정이므로 최소 하나의 연결된 계정을 유지해야 합니다", "40085": "주문 금액이 너무 작아 결제를 계속할 수 없습니다" - } -} + }, + "save": "저장" +} \ No newline at end of file diff --git a/frontend/public/locales/ko-KR/dashboard.json b/frontend/public/locales/ko-KR/dashboard.json index a3c76b09..ff49c4df 100644 --- a/frontend/public/locales/ko-KR/dashboard.json +++ b/frontend/public/locales/ko-KR/dashboard.json @@ -1421,7 +1421,8 @@ "otherConditions": "기타 조건", "shareLinkExisted": "공유 링크 존재", "directLinkExisted": "중계 직접 링크 존재", - "isUploading": "업로드 중" + "isUploading": "업로드 중", + "selectUser": "사용자 선택" }, "entity": { "refenenceCount": "참조 횟수", @@ -1467,7 +1468,8 @@ "rawContent": "원본 기록", "confirmDelete": "이 이벤트를 삭제하시겠습니까?", "deleteXEvents": "{{num}}개 이벤트 삭제", - "confirmBatchDelete": "{{num}}개 이벤트를 삭제하시겠습니까?" + "confirmBatchDelete": "{{num}}개 이벤트를 삭제하시겠습니까?", + "allEventTypes": "모든 이벤트 유형" }, "share": { "confirmBatchDelete": "{{num}}개 공유를 삭제하시겠습니까?", @@ -1755,4 +1757,4 @@ "shareID": "공유 ID", "reason": "원인" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/pl-PL/application.json b/frontend/public/locales/pl-PL/application.json index 94b1a62f..5a87802e 100644 --- a/frontend/public/locales/pl-PL/application.json +++ b/frontend/public/locales/pl-PL/application.json @@ -611,7 +611,9 @@ "viewSetting": "Ustawienia widoku", "saved": "Zapisano", "notSet": "Nieustawione", - "deleteViewSetting": "Usuń ustawienia widoku" + "deleteViewSetting": "Usuń ustawienia widoku", + "anonymous": "Anonimowy", + "shareTargetsCount": "{{count}} cele" }, "uploader": { "fileCopyName": "Kopia ", @@ -994,8 +996,13 @@ "validDurationDays": "{{num}} dni", "reportTarget": "Cel zgłoszenia", "reportReason": "Powód", - "reportReasonOptions": ["Naruszenie praw autorskich", "Szkodliwa treść", "Spam", "Inne"], + "reportReasonOptions": [ + "Naruszenie praw autorskich", + "Szkodliwa treść", + "Spam", + "Inne" + ], "reportDescription": "Dodatkowy opis", "reportAbuseSuccess": "Zgłoszenie przesłane." } -} +} \ No newline at end of file diff --git a/frontend/public/locales/pl-PL/common.json b/frontend/public/locales/pl-PL/common.json index 77c7d365..e0582117 100644 --- a/frontend/public/locales/pl-PL/common.json +++ b/frontend/public/locales/pl-PL/common.json @@ -102,5 +102,6 @@ "50005": "Błąd wewnętrzny.", "50010": "Żądany węzeł jest niedostępny.", "50011": "Nie udało się pobrać metadanych pliku." - } -} + }, + "save": "Zapisz" +} \ No newline at end of file diff --git a/frontend/public/locales/pl-PL/dashboard.json b/frontend/public/locales/pl-PL/dashboard.json index 6a6e246a..4523c95b 100644 --- a/frontend/public/locales/pl-PL/dashboard.json +++ b/frontend/public/locales/pl-PL/dashboard.json @@ -1422,7 +1422,8 @@ "otherConditions": "Inne warunki", "shareLinkExisted": "Posiada link udostępniania", "directLinkExisted": "Posiada link bezpośredni", - "isUploading": "Jest przesyłany" + "isUploading": "Jest przesyłany", + "selectUser": "Wybierz użytkownika" }, "entity": { "refenenceCount": "Liczba odniesień", @@ -1758,4 +1759,4 @@ "shareID": "ID Udostępnienia", "reason": "Powód" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/pt-BR/application.json b/frontend/public/locales/pt-BR/application.json index 13fd3ec5..7df16f05 100644 --- a/frontend/public/locales/pt-BR/application.json +++ b/frontend/public/locales/pt-BR/application.json @@ -529,7 +529,7 @@ "everyone": "Todos os outros", "otherGroup": "Outros grupos", "sameGroup": "Mesmo grupo que eu", - "anonymous": "Visitantes anônimos", + "anonymous": "Anônimo", "noResults": "Nenhum resultado", "searchGroupUser": "Buscar por e-mails ou grupos...", "resetToDefault": "Redefinir para padrão", @@ -664,7 +664,8 @@ "viewSetting": "Configuração de visualização", "saved": "Salvo", "notSet": "Não definido", - "deleteViewSetting": "Excluir configuração de visualização" + "deleteViewSetting": "Excluir configuração de visualização", + "shareTargetsCount": "{{count}} destinos" }, "uploader": { "fileCopyName": "Cópia de ", @@ -1110,7 +1111,12 @@ "report": "Denunciar abuso", "reportTarget": "Alvo da denúncia", "reportReason": "Razão", - "reportReasonOptions": ["Violação de direitos autorais", "Conteúdo prejudicial", "Spam", "Outro"], + "reportReasonOptions": [ + "Violação de direitos autorais", + "Conteúdo prejudicial", + "Spam", + "Outro" + ], "reportDescription": "Descrição adicional", "reportAbuseSuccess": "Denúncia enviada.", "migrateStoragePolicy": "Migrar política de armazenamento", @@ -1189,4 +1195,4 @@ "openPaymentLink": "Abrir link de pagamento", "creditReasonAdjust": "Ajuste manual" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/pt-BR/common.json b/frontend/public/locales/pt-BR/common.json index 6ec9a132..08cf7155 100644 --- a/frontend/public/locales/pt-BR/common.json +++ b/frontend/public/locales/pt-BR/common.json @@ -116,5 +116,6 @@ "40072": "Você é administrador, não pode comprar outro grupo.", "40084": "Sua conta não possui senha, você deve manter pelo menos uma conta vinculada.", "40085": "O valor total deste pedido é muito pequeno para checkout." - } -} + }, + "save": "Salvar" +} \ No newline at end of file diff --git a/frontend/public/locales/pt-BR/dashboard.json b/frontend/public/locales/pt-BR/dashboard.json index f998a5d0..aad48f0c 100644 --- a/frontend/public/locales/pt-BR/dashboard.json +++ b/frontend/public/locales/pt-BR/dashboard.json @@ -1421,7 +1421,8 @@ "otherConditions": "Outras condições", "shareLinkExisted": "Tem link de compartilhamento", "directLinkExisted": "Tem link direto", - "isUploading": "Está enviando" + "isUploading": "Está enviando", + "selectUser": "Selecionar usuário" }, "entity": { "refenenceCount": "Contagem de referência", @@ -1467,7 +1468,8 @@ "rawContent": "Conteúdo bruto", "confirmDelete": "Tem certeza de que deseja excluir este evento?", "deleteXEvents": "Excluir {{num}} eventos", - "confirmBatchDelete": "Tem certeza de que deseja excluir {{num}} eventos?" + "confirmBatchDelete": "Tem certeza de que deseja excluir {{num}} eventos?", + "allEventTypes": "Todos os tipos de evento" }, "share": { "confirmBatchDelete": "Tem certeza de que deseja excluir {{num}} compartilhamentos?", @@ -1756,4 +1758,4 @@ "shareID": "ID compartilhado", "reason": "Razão" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/ru-RU/application.json b/frontend/public/locales/ru-RU/application.json index 52f6e154..d8765a59 100644 --- a/frontend/public/locales/ru-RU/application.json +++ b/frontend/public/locales/ru-RU/application.json @@ -529,7 +529,7 @@ "everyone": "Все остальные", "otherGroup": "Другие группы пользователей", "sameGroup": "Та же группа пользователей, что и я", - "anonymous": "Анонимные посетители", + "anonymous": "Анонимный", "noResults": "Нет результатов", "searchGroupUser": "Поиск пользователей или групп пользователей...", "resetToDefault": "Сбросить к значениям по умолчанию", @@ -664,7 +664,8 @@ "viewSetting": "Настройки просмотра", "saved": "Сохранено", "notSet": "Не установлено", - "deleteViewSetting": "Удалить настройки просмотра" + "deleteViewSetting": "Удалить настройки просмотра", + "shareTargetsCount": "{{count}} целей" }, "uploader": { "fileCopyName": "Копия_", @@ -1110,7 +1111,12 @@ "report": "Сообщить о злоупотреблении", "reportTarget": "Объект жалобы", "reportReason": "Причина", - "reportReasonOptions": ["Нарушение авторских прав", "Вредоносный контент", "Спам", "Другое"], + "reportReasonOptions": [ + "Нарушение авторских прав", + "Вредоносный контент", + "Спам", + "Другое" + ], "reportDescription": "Дополнительное описание", "reportAbuseSuccess": "Жалоба подана", "migrateStoragePolicy": "Перенести политику хранения", @@ -1189,4 +1195,4 @@ "openPaymentLink": "Открыть платёжную ссылку напрямую", "creditReasonAdjust": "Ручная корректировка" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/ru-RU/common.json b/frontend/public/locales/ru-RU/common.json index 775bea43..af963828 100644 --- a/frontend/public/locales/ru-RU/common.json +++ b/frontend/public/locales/ru-RU/common.json @@ -116,5 +116,6 @@ "40072": "Администратор не может перейти в другую группу пользователей", "40084": "Ваш аккаунт является беспарольным, вы должны оставить хотя бы один связанный аккаунт", "40085": "Сумма заказа слишком мала для продолжения оплаты" - } -} + }, + "save": "Сохранить" +} \ No newline at end of file diff --git a/frontend/public/locales/ru-RU/dashboard.json b/frontend/public/locales/ru-RU/dashboard.json index 847796e6..73747e4d 100644 --- a/frontend/public/locales/ru-RU/dashboard.json +++ b/frontend/public/locales/ru-RU/dashboard.json @@ -1422,7 +1422,8 @@ "otherConditions": "Другие условия", "shareLinkExisted": "Существует ссылка для общего доступа", "directLinkExisted": "Существует прямая ссылка", - "isUploading": "Загружается" + "isUploading": "Загружается", + "selectUser": "Выбрать пользователя" }, "entity": { "refenenceCount": "Количество ссылок", @@ -1468,7 +1469,8 @@ "rawContent": "Исходная запись", "confirmDelete": "Подтвердите удаление этого события?", "deleteXEvents": "Удалить {{num}} событий", - "confirmBatchDelete": "Подтвердите удаление {{num}} событий?" + "confirmBatchDelete": "Подтвердите удаление {{num}} событий?", + "allEventTypes": "Все типы событий" }, "share": { "confirmBatchDelete": "Подтвердите удаление {{num}} общих доступов?", @@ -1757,4 +1759,4 @@ "shareID": "ID общего ресурса", "reason": "Причина" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/zh-CN/application.json b/frontend/public/locales/zh-CN/application.json index 84a19479..0a2718d4 100644 --- a/frontend/public/locales/zh-CN/application.json +++ b/frontend/public/locales/zh-CN/application.json @@ -703,7 +703,9 @@ "publicListing": "收录到公开目录", "publicListingDes": "将此分享显示在“发现”页,任何人(包括未登录访客)可见。带密码的分享不可用。", "hideReadme": "隐藏 README 文件", - "hideReadmeDes": "在分享文件列表中隐藏 README 文件本身,访客仍可看到渲染后的说明内容。" + "hideReadmeDes": "在分享文件列表中隐藏 README 文件本身,访客仍可看到渲染后的说明内容。", + "anonymous": "匿名", + "shareTargetsCount": "{{count}} 个目标" }, "uploader": { "fileCopyName": "副本_", @@ -1218,4 +1220,4 @@ "discover": { "searchPlaceholder": "搜索公开分享" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/zh-CN/common.json b/frontend/public/locales/zh-CN/common.json index 7e1da725..8e9b9c9d 100644 --- a/frontend/public/locales/zh-CN/common.json +++ b/frontend/public/locales/zh-CN/common.json @@ -106,5 +106,6 @@ "50011": "文件元信息查询失败" }, "delete": "删除", - "40017": "该账号已被封禁。{{message}}" -} + "40017": "该账号已被封禁。{{message}}", + "save": "保存" +} \ No newline at end of file diff --git a/frontend/public/locales/zh-CN/dashboard.json b/frontend/public/locales/zh-CN/dashboard.json index c15f0d93..365f46bd 100644 --- a/frontend/public/locales/zh-CN/dashboard.json +++ b/frontend/public/locales/zh-CN/dashboard.json @@ -1611,7 +1611,8 @@ "isUploading": "上传中", "deletedState": "删除状态", "liveOnly": "仅未删除", - "trashOnly": "仅回收站" + "trashOnly": "仅回收站", + "selectUser": "选择用户" }, "entity": { "refenenceCount": "引用次数", @@ -1666,7 +1667,8 @@ "noEvents": "暂无事件记录", "confirmDelete": "确认要删除此事件?", "deleteXEvents": "删除 {{num}} 个事件", - "confirmBatchDelete": "确认要删除 {{num}} 个事件?" + "confirmBatchDelete": "确认要删除 {{num}} 个事件?", + "allEventTypes": "全部事件类型" }, "share": { "confirmBatchDelete": "确认要删除 {{num}} 个分享?", diff --git a/frontend/public/locales/zh-TW/application.json b/frontend/public/locales/zh-TW/application.json index ccd2bdd5..2fe01962 100644 --- a/frontend/public/locales/zh-TW/application.json +++ b/frontend/public/locales/zh-TW/application.json @@ -611,7 +611,9 @@ "viewSetting": "視圖設定", "saved": "已保存", "notSet": "未設定", - "deleteViewSetting": "刪除視圖設定" + "deleteViewSetting": "刪除視圖設定", + "anonymous": "匿名", + "shareTargetsCount": "{{count}} 個目標" }, "uploader": { "fileCopyName": "副本_", @@ -991,8 +993,13 @@ "validDurationDays": "{{num}} 天", "reportTarget": "舉報對象", "reportReason": "原因", - "reportReasonOptions": ["侵權", "有害內容", "垃圾訊息", "其他"], + "reportReasonOptions": [ + "侵權", + "有害內容", + "垃圾訊息", + "其他" + ], "reportDescription": "補充說明", "reportAbuseSuccess": "舉報已提交" } -} +} \ No newline at end of file diff --git a/frontend/public/locales/zh-TW/common.json b/frontend/public/locales/zh-TW/common.json index 5fe5c219..ab4d2417 100644 --- a/frontend/public/locales/zh-TW/common.json +++ b/frontend/public/locales/zh-TW/common.json @@ -102,5 +102,6 @@ "50005": "內部錯誤 ({{message}})", "50010": "目標節點不可用", "50011": "檔案元資訊查詢失敗" - } -} + }, + "save": "儲存" +} \ No newline at end of file diff --git a/frontend/public/locales/zh-TW/dashboard.json b/frontend/public/locales/zh-TW/dashboard.json index b7d58c50..ec0d1e87 100644 --- a/frontend/public/locales/zh-TW/dashboard.json +++ b/frontend/public/locales/zh-TW/dashboard.json @@ -1422,7 +1422,8 @@ "otherConditions": "其他條件", "shareLinkExisted": "有分享連結", "directLinkExisted": "有中轉直鏈", - "isUploading": "上傳中" + "isUploading": "上傳中", + "selectUser": "選擇使用者" }, "entity": { "refenenceCount": "引用次數", @@ -1442,7 +1443,7 @@ "cleanupNotAfter": "在此日期之前", "cleanupEventTypes": "事件類型", "cleanupEventTypesDes": "選擇要清理的事件類型,留空表示清理所有類型。", - "allEventTypes": "所有事件類型", + "allEventTypes": "全部事件類型", "initiator": "發起者", "event": "事件", "userID": "使用者 ID", @@ -1758,4 +1759,4 @@ "shareID": "分享 ID", "reason": "原因" } -} +} \ No newline at end of file diff --git a/frontend/src/component/Pages/Tasks/TaskProps.tsx b/frontend/src/component/Pages/Tasks/TaskProps.tsx index b533e662..8492c57e 100644 --- a/frontend/src/component/Pages/Tasks/TaskProps.tsx +++ b/frontend/src/component/Pages/Tasks/TaskProps.tsx @@ -61,7 +61,7 @@ export const getTaskStatusText = (status: TaskStatus, t: TFunction) => { case TaskStatus.completed: return t("application:setting.finished"); default: - return t("application:uplaoder.unknownStatus"); + return t("application:uploader.unknownStatus"); } }; diff --git a/frontend/src/component/Uploader/Uploader.tsx b/frontend/src/component/Uploader/Uploader.tsx index d8c26b26..e9fffcb1 100644 --- a/frontend/src/component/Uploader/Uploader.tsx +++ b/frontend/src/component/Uploader/Uploader.tsx @@ -125,7 +125,7 @@ const Uploader = () => { if (e instanceof UploaderError) { enqueueSnackbar(e.Message(), { variant: "warning" }); } else { - enqueueSnackbar(t("uploader:unknownError", { msg: e.message }), { + enqueueSnackbar(t("application:uploader.unknownError", { msg: e.message }), { variant: "error", }); }