feat(frontend): i18n expansion, admin/profile i18n, grid/list view fix, empty state
- Add 5 new locales (hi, ar, ru, ja, ko) — now 14 total - Admin panel: 117 i18n keys, confirm modal, animated tabs, no inline handlers - Profile page: 58 i18n keys with data-i18n attributes - Fix i18n safeT() shadowing bug and translationsLoaded timing - Fix grid/list view: list header no longer shows in grid mode on login - Fix classList.toggle hidden sync for view switching across all nav functions - Revert .hidden important that broke login page rendering - Add files empty state (no_files + empty_hint) with translations - Fix language selector dropdown scroll and styling - Fix admin panel scroll with sticky tabs
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
use crate::application::dtos::app_password_dto::CreateAppPasswordRequestDto;
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
use axum::extract::State;
|
||||
use axum::routing::{delete, get, post};
|
||||
use axum::{Json, Router};
|
||||
@@ -26,7 +26,7 @@ pub fn app_password_routes() -> Router<Arc<AppState>> {
|
||||
/// Returns the plain-text password ONCE. The user must copy it immediately.
|
||||
async fn create_app_password(
|
||||
State(state): State<Arc<AppState>>,
|
||||
axum::Extension(user): axum::Extension<CurrentUser>,
|
||||
user: AuthUser,
|
||||
Json(request): Json<CreateAppPasswordRequestDto>,
|
||||
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordCreatedResponseDto>, AppError>
|
||||
{
|
||||
@@ -48,7 +48,7 @@ async fn create_app_password(
|
||||
/// Never returns plain-text passwords (only prefix + metadata).
|
||||
async fn list_app_passwords(
|
||||
State(state): State<Arc<AppState>>,
|
||||
axum::Extension(user): axum::Extension<CurrentUser>,
|
||||
user: AuthUser,
|
||||
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordListResponseDto>, AppError>
|
||||
{
|
||||
let service = state
|
||||
@@ -64,7 +64,7 @@ async fn list_app_passwords(
|
||||
/// DELETE /api/auth/app-passwords/:id — Revoke an app password.
|
||||
async fn revoke_app_password(
|
||||
State(state): State<Arc<AppState>>,
|
||||
axum::Extension(user): axum::Extension<CurrentUser>,
|
||||
user: AuthUser,
|
||||
axum::extract::Path(id): axum::extract::Path<String>,
|
||||
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordRevokeResponseDto>, AppError>
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::application::ports::calendar_ports::CalendarUseCase;
|
||||
use crate::application::services::calendar_service::CalendarService;
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, CurrentUser};
|
||||
|
||||
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
||||
|
||||
@@ -138,10 +138,11 @@ fn reject_path_traversal(path: &str) -> Result<(), AppError> {
|
||||
|
||||
// ─── Helper: extract user from request ───────────────────────────────
|
||||
|
||||
fn extract_user(req: &Request<Body>) -> Result<CurrentUser, AppError> {
|
||||
fn extract_user(req: &Request<Body>) -> Result<AuthUser, AppError> {
|
||||
req.extensions()
|
||||
.get::<Arc<CurrentUser>>()
|
||||
.map(|arc| (**arc).clone())
|
||||
.cloned()
|
||||
.map(AuthUser)
|
||||
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::application::ports::carddav_ports::{AddressBookUseCase, ContactUseCas
|
||||
use crate::common::di::AppState;
|
||||
use crate::infrastructure::adapters::contact_storage_adapter::ContactStorageAdapter;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, CurrentUser};
|
||||
|
||||
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
||||
|
||||
@@ -126,10 +126,11 @@ fn reject_path_traversal(path: &str) -> Result<(), AppError> {
|
||||
|
||||
// ─── Helper: extract user from request ───────────────────────────────
|
||||
|
||||
fn extract_user(req: &Request<Body>) -> Result<CurrentUser, AppError> {
|
||||
fn extract_user(req: &Request<Body>) -> Result<AuthUser, AppError> {
|
||||
req.extensions()
|
||||
.get::<Arc<CurrentUser>>()
|
||||
.map(|arc| (**arc).clone())
|
||||
.cloned()
|
||||
.map(AuthUser)
|
||||
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ use crate::application::services::folder_service::FolderService;
|
||||
use crate::common::di::AppState;
|
||||
use crate::infrastructure::services::path_resolver_service::ResolvedResource;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, CurrentUser};
|
||||
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, percent_decode_str, utf8_percent_encode};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -93,10 +93,11 @@ const PROPFIND_BATCH_SIZE: i64 = 500;
|
||||
/// Every mutating or data-returning WebDAV handler **must** call this so
|
||||
/// that the real `user.id` is available for ownership checks and for the
|
||||
/// user-scoped `PathResolverService` methods.
|
||||
fn extract_user(req: &Request<Body>) -> Result<CurrentUser, AppError> {
|
||||
fn extract_user(req: &Request<Body>) -> Result<AuthUser, AppError> {
|
||||
req.extensions()
|
||||
.get::<Arc<CurrentUser>>()
|
||||
.map(|arc| (**arc).clone())
|
||||
.cloned()
|
||||
.map(AuthUser)
|
||||
.ok_or_else(|| AppError::unauthorized("Authentication required"))
|
||||
}
|
||||
|
||||
|
||||
@@ -413,14 +413,12 @@ async fn authorize_wopi_access<S: FileRetrievalUseCase>(
|
||||
/// This endpoint is behind normal auth middleware. The authenticated user
|
||||
/// requests a WOPI session for a specific file.
|
||||
pub async fn get_editor_url(
|
||||
AuthUser {
|
||||
id: user_id,
|
||||
username,
|
||||
..
|
||||
}: AuthUser,
|
||||
auth_user: AuthUser,
|
||||
Query(params): Query<EditorUrlParams>,
|
||||
State(state): State<WopiState>,
|
||||
) -> Response {
|
||||
let user_id = auth_user.id;
|
||||
let username = &auth_user.username;
|
||||
// Verify the caller owns the file (SQL-level check, no existence leak).
|
||||
let (file, can_write) = match authorize_wopi_access(
|
||||
state.app_state.applications.file_retrieval_service.as_ref(),
|
||||
|
||||
@@ -20,12 +20,18 @@ use crate::application::ports::auth_ports::TokenServicePort;
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CookieAuthenticated;
|
||||
|
||||
// Structure for use in Axum extractors
|
||||
// Newtype over Arc<CurrentUser> for zero-allocation extraction.
|
||||
// `Deref<Target = CurrentUser>` lets handlers access `.id`, `.username`,
|
||||
// `.email`, `.role` transparently — no signature changes needed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AuthUser {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
pub role: String,
|
||||
pub struct AuthUser(pub Arc<CurrentUser>);
|
||||
|
||||
impl std::ops::Deref for AuthUser {
|
||||
type Target = CurrentUser;
|
||||
#[inline]
|
||||
fn deref(&self) -> &CurrentUser {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Reusable extractor that gets the user_id of the authenticated user.
|
||||
@@ -38,7 +44,8 @@ pub struct AuthUser {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CurrentUserId(pub Uuid);
|
||||
|
||||
// Implement FromRequestParts for AuthUser — allows using `auth_user: AuthUser` in handlers
|
||||
// Implement FromRequestParts for AuthUser — allows using `auth_user: AuthUser` in handlers.
|
||||
// Cost: 1 atomic increment (~1 ns) instead of 3 String clones (~100 ns + 3 mallocs).
|
||||
impl<S> FromRequestParts<S> for AuthUser
|
||||
where
|
||||
S: Send + Sync,
|
||||
@@ -49,29 +56,8 @@ where
|
||||
parts
|
||||
.extensions
|
||||
.get::<Arc<CurrentUser>>()
|
||||
.map(|cu| AuthUser {
|
||||
id: cu.id,
|
||||
username: cu.username.clone(),
|
||||
role: cu.role.clone(),
|
||||
})
|
||||
.ok_or(AuthError::UserNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
// Implement FromRequestParts for CurrentUser — full user extractor from extensions
|
||||
// The middleware inserts Arc<CurrentUser>; this extractor cheaply clones the Arc
|
||||
// (~1 ns atomic increment) instead of deep-cloning 4 Strings (~60-100 ns).
|
||||
impl<S> FromRequestParts<S> for CurrentUser
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = AuthError;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
parts
|
||||
.extensions
|
||||
.get::<Arc<CurrentUser>>()
|
||||
.map(|arc| (**arc).clone())
|
||||
.cloned()
|
||||
.map(AuthUser)
|
||||
.ok_or(AuthError::UserNotFound)
|
||||
}
|
||||
}
|
||||
@@ -113,27 +99,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional auth user extractor – never fails.
|
||||
/// Yields `Some(AuthUser)` when auth middleware ran, `None` otherwise.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OptionalAuthUser(pub Option<AuthUser>);
|
||||
|
||||
impl<S> FromRequestParts<S> for OptionalAuthUser
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Infallible;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(OptionalAuthUser(parts.extensions.get::<Arc<CurrentUser>>().map(
|
||||
|cu| AuthUser {
|
||||
id: cu.id,
|
||||
username: cu.username.clone(),
|
||||
role: cu.role.clone(),
|
||||
},
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
// Error for authentication operations
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::application::dtos::search_dto::SearchCriteriaDto;
|
||||
use crate::application::ports::inbound::SearchUseCase;
|
||||
use crate::application::ports::storage_ports::StorageUsagePort;
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
|
||||
/// Build an OCS success response with the given statuscode and data.
|
||||
fn ocs_ok(statuscode: u16, data: serde_json::Value) -> serde_json::Value {
|
||||
@@ -45,7 +45,7 @@ pub async fn handle_capabilities_v2(State(state): State<Arc<AppState>>) -> Respo
|
||||
Json(payload).into_response()
|
||||
}
|
||||
|
||||
pub async fn handle_user_info(State(state): State<Arc<AppState>>, user: CurrentUser) -> Response {
|
||||
pub async fn handle_user_info(State(state): State<Arc<AppState>>, user: AuthUser) -> Response {
|
||||
let quota: (i64, i64) = match state.storage_usage_service.as_ref() {
|
||||
Some(service) => match service.get_user_storage_info(user.id).await {
|
||||
Ok((used, total)) => (used, total),
|
||||
@@ -86,7 +86,7 @@ pub async fn handle_user_info(State(state): State<Arc<AppState>>, user: CurrentU
|
||||
pub async fn handle_user_provisioning_v1(
|
||||
state: State<Arc<AppState>>,
|
||||
path: Path<String>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
) -> Response {
|
||||
user_provisioning_response(state, path, user, 1).await
|
||||
}
|
||||
@@ -95,7 +95,7 @@ pub async fn handle_user_provisioning_v1(
|
||||
pub async fn handle_user_provisioning_v2(
|
||||
state: State<Arc<AppState>>,
|
||||
path: Path<String>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
) -> Response {
|
||||
user_provisioning_response(state, path, user, 2).await
|
||||
}
|
||||
@@ -105,7 +105,7 @@ pub async fn handle_user_provisioning_v2(
|
||||
async fn user_provisioning_response(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(userid): Path<String>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
ocs_version: u8,
|
||||
) -> Response {
|
||||
let statuscode = if ocs_version == 1 { 100 } else { 200 };
|
||||
@@ -197,7 +197,7 @@ async fn user_provisioning_response(
|
||||
|
||||
pub async fn handle_revoke_apppassword(
|
||||
State(state): State<Arc<AppState>>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
headers: axum::http::HeaderMap,
|
||||
) -> Response {
|
||||
let nextcloud = match state.nextcloud.as_ref() {
|
||||
@@ -249,7 +249,7 @@ pub async fn handle_recommendations() -> Response {
|
||||
/// this endpoint and expects a well-formed OCS response rather than a 404.
|
||||
pub async fn handle_sharees_search(
|
||||
State(state): State<Arc<AppState>>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
axum::extract::Query(params): axum::extract::Query<ShareeSearchParams>,
|
||||
) -> Response {
|
||||
let search = params.search.unwrap_or_default();
|
||||
@@ -343,7 +343,7 @@ pub async fn handle_search(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(provider_id): Path<String>,
|
||||
axum::extract::Query(params): axum::extract::Query<UnifiedSearchParams>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
) -> Response {
|
||||
// Only the "files" provider is supported
|
||||
if provider_id != "files" {
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::application::ports::file_ports::FileRetrievalUseCase;
|
||||
use crate::application::ports::storage_ports::FileReadPort;
|
||||
use crate::application::ports::thumbnail_ports::{ThumbnailPort, ThumbnailSize};
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PreviewParams {
|
||||
@@ -34,7 +34,7 @@ pub struct PreviewParams {
|
||||
/// - Size selection based on request dimensions and forceIcon param
|
||||
pub async fn handle_preview(
|
||||
State(state): State<Arc<AppState>>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
Query(params): Query<PreviewParams>,
|
||||
) -> impl IntoResponse {
|
||||
// Parse the Nextcloud file ID — the NC app may append an instance suffix
|
||||
|
||||
@@ -10,7 +10,7 @@ use axum::{
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, CurrentUser};
|
||||
use crate::interfaces::middleware::rate_limit::{RateLimiter, rate_limit_login};
|
||||
use crate::interfaces::nextcloud::avatar_handler;
|
||||
use crate::interfaces::nextcloud::basic_auth_middleware::basic_auth_middleware;
|
||||
@@ -176,7 +176,7 @@ fn verify_url_user(url_user: &str, auth_user: &CurrentUser) -> Result<(), Respon
|
||||
async fn handle_dav_files(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path((url_user, subpath)): Path<(String, String)>,
|
||||
user_ext: CurrentUser,
|
||||
user_ext: AuthUser,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response, Response> {
|
||||
verify_url_user(&url_user, &user_ext)?;
|
||||
@@ -188,7 +188,7 @@ async fn handle_dav_files(
|
||||
async fn handle_dav_files_root(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(url_user): Path<String>,
|
||||
user_ext: CurrentUser,
|
||||
user_ext: AuthUser,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response, Response> {
|
||||
verify_url_user(&url_user, &user_ext)?;
|
||||
@@ -200,7 +200,7 @@ async fn handle_dav_files_root(
|
||||
async fn handle_dav_uploads(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path((url_user, upload_id, rest)): Path<(String, String, String)>,
|
||||
user_ext: CurrentUser,
|
||||
user_ext: AuthUser,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response, Response> {
|
||||
verify_url_user(&url_user, &user_ext)?;
|
||||
@@ -212,7 +212,7 @@ async fn handle_dav_uploads(
|
||||
async fn handle_dav_uploads_root(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path((url_user, upload_id)): Path<(String, String)>,
|
||||
user_ext: CurrentUser,
|
||||
user_ext: AuthUser,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response, Response> {
|
||||
verify_url_user(&url_user, &user_ext)?;
|
||||
@@ -222,7 +222,7 @@ async fn handle_dav_uploads_root(
|
||||
}
|
||||
|
||||
/// Legacy /remote.php/webdav/* — redirect to /remote.php/dav/files/{user}/*
|
||||
async fn handle_legacy_webdav(Path(subpath): Path<String>, user_ext: CurrentUser) -> Response {
|
||||
async fn handle_legacy_webdav(Path(subpath): Path<String>, user_ext: AuthUser) -> Response {
|
||||
let location = format!("/remote.php/dav/files/{}/{}", user_ext.username, subpath);
|
||||
Response::builder()
|
||||
.status(StatusCode::MOVED_PERMANENTLY)
|
||||
@@ -231,7 +231,7 @@ async fn handle_legacy_webdav(Path(subpath): Path<String>, user_ext: CurrentUser
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn handle_legacy_webdav_root(user_ext: CurrentUser) -> Response {
|
||||
async fn handle_legacy_webdav_root(user_ext: AuthUser) -> Response {
|
||||
let location = format!("/remote.php/dav/files/{}/", user_ext.username);
|
||||
Response::builder()
|
||||
.status(StatusCode::MOVED_PERMANENTLY)
|
||||
@@ -243,7 +243,7 @@ async fn handle_legacy_webdav_root(user_ext: CurrentUser) -> Response {
|
||||
async fn handle_dav_trashbin(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path((url_user, subpath)): Path<(String, String)>,
|
||||
user_ext: CurrentUser,
|
||||
user_ext: AuthUser,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response, Response> {
|
||||
verify_url_user(&url_user, &user_ext)?;
|
||||
@@ -255,7 +255,7 @@ async fn handle_dav_trashbin(
|
||||
async fn handle_dav_trashbin_root(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(url_user): Path<String>,
|
||||
user_ext: CurrentUser,
|
||||
user_ext: AuthUser,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response, Response> {
|
||||
verify_url_user(&url_user, &user_ext)?;
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::sync::Arc;
|
||||
use crate::application::ports::trash_ports::TrashUseCase;
|
||||
use crate::common::di::AppState;
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, CurrentUser};
|
||||
use crate::interfaces::nextcloud::webdav_handler::{
|
||||
format_oc_id, resolve_file_id, resolve_folder_id, write_text_element,
|
||||
};
|
||||
@@ -25,7 +25,7 @@ const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
|
||||
pub async fn handle_nc_trashbin(
|
||||
state: Arc<AppState>,
|
||||
req: Request<Body>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
subpath: String,
|
||||
) -> Result<Response<Body>, AppError> {
|
||||
let method = req.method().clone();
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::application::ports::file_ports::{FileRetrievalUseCase, FileUploadUseC
|
||||
use crate::common::di::AppState;
|
||||
use crate::common::mime_detect::{filename_from_path, refine_content_type_from_file};
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, CurrentUser};
|
||||
|
||||
/// Dispatch Nextcloud chunked upload WebDAV requests.
|
||||
///
|
||||
@@ -21,7 +21,7 @@ use crate::interfaces::middleware::auth::CurrentUser;
|
||||
pub async fn handle_nc_uploads(
|
||||
state: Arc<AppState>,
|
||||
req: Request<Body>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
upload_id: String,
|
||||
rest: String, // chunk name or ".file" or empty
|
||||
) -> Result<Response<Body>, AppError> {
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::application::ports::trash_ports::TrashUseCase;
|
||||
use crate::common::di::AppState;
|
||||
use crate::common::mime_detect::{filename_from_path, refine_content_type};
|
||||
use crate::interfaces::errors::AppError;
|
||||
use crate::interfaces::middleware::auth::CurrentUser;
|
||||
use crate::interfaces::middleware::auth::{AuthUser, CurrentUser};
|
||||
|
||||
/// Extension trait to map XML write errors to `String` concisely.
|
||||
trait XmlResultExt<T> {
|
||||
@@ -89,7 +89,7 @@ pub fn nc_href(username: &str, subpath: &str) -> String {
|
||||
pub async fn handle_nc_webdav(
|
||||
state: Arc<AppState>,
|
||||
req: Request<Body>,
|
||||
user: CurrentUser,
|
||||
user: AuthUser,
|
||||
subpath: String,
|
||||
) -> Result<Response<Body>, AppError> {
|
||||
let method = req.method().clone();
|
||||
|
||||
+99
-86
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>OxiCloud — Admin Panel</title>
|
||||
<script src="/js/core/theme-init.js"></script>
|
||||
<script src="/js/core/i18n.js" defer></script>
|
||||
<script src="/js/core/icons.js" defer></script>
|
||||
<script src="/js/core/formatters.js" defer></script>
|
||||
<script src="/js/core/csrf.js" defer></script>
|
||||
@@ -24,22 +25,22 @@
|
||||
</div>
|
||||
<span class="admin-title-text">OxiCloud</span>
|
||||
</a>
|
||||
<span class="admin-title-separator">· Admin</span>
|
||||
<span class="admin-title-separator">· <span data-i18n="admin.tab_dashboard">Admin</span></span>
|
||||
</div>
|
||||
<div class="admin-header-right">
|
||||
<a href="/"
|
||||
><i class="fas fa-arrow-left"></i> Back to OxiCloud</a
|
||||
><i class="fas fa-arrow-left"></i> <span data-i18n="admin.back_to_app">Back to OxiCloud</span></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-container">
|
||||
<div id="loading"><i class="fas fa-circle-notch"></i> Loading…</div>
|
||||
<div id="loading"><i class="fas fa-circle-notch"></i> <span data-i18n="admin.loading">Loading…</span></div>
|
||||
<div id="access-denied">
|
||||
<div class="access-icon"><i class="fas fa-lock"></i></div>
|
||||
<h2>Access Denied</h2>
|
||||
<p>Administrator privileges required to access this panel.</p>
|
||||
<a href="/login"><i class="fas fa-sign-in-alt"></i> Sign in</a>
|
||||
<h2 data-i18n="admin.access_denied">Access Denied</h2>
|
||||
<p data-i18n="admin.access_denied_desc">Administrator privileges required to access this panel.</p>
|
||||
<a href="/login"><i class="fas fa-sign-in-alt"></i> <span data-i18n="admin.sign_in">Sign in</span></a>
|
||||
</div>
|
||||
|
||||
<div id="main-content">
|
||||
@@ -48,13 +49,13 @@
|
||||
class="admin-tab active"
|
||||
id="tab-btn-dashboard"
|
||||
>
|
||||
<i class="fas fa-chart-pie"></i> Dashboard
|
||||
<i class="fas fa-chart-pie"></i> <span data-i18n="admin.tab_dashboard">Dashboard</span>
|
||||
</button>
|
||||
<button class="admin-tab" id="tab-btn-users">
|
||||
<i class="fas fa-users"></i> Users
|
||||
<i class="fas fa-users"></i> <span data-i18n="admin.tab_users">Users</span>
|
||||
</button>
|
||||
<button class="admin-tab" id="tab-btn-oidc">
|
||||
<i class="fas fa-key"></i> SSO / OIDC
|
||||
<i class="fas fa-key"></i> <span data-i18n="admin.tab_oidc">SSO / OIDC</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -67,7 +68,7 @@
|
||||
>
|
||||
—
|
||||
</div>
|
||||
<div class="stat-label">Total Users</div>
|
||||
<div class="stat-label" data-i18n="admin.total_users">Total Users</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div
|
||||
@@ -76,7 +77,7 @@
|
||||
>
|
||||
—
|
||||
</div>
|
||||
<div class="stat-label">Active Users</div>
|
||||
<div class="stat-label" data-i18n="admin.active_users">Active Users</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div
|
||||
@@ -85,30 +86,30 @@
|
||||
>
|
||||
—
|
||||
</div>
|
||||
<div class="stat-label">Admins</div>
|
||||
<div class="stat-label" data-i18n="admin.admins">Admins</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="ds-version">—</div>
|
||||
<div class="stat-label">Version</div>
|
||||
<div class="stat-label" data-i18n="admin.version">Version</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-card">
|
||||
<h2><i class="fas fa-hdd"></i> Storage Overview</h2>
|
||||
<h2><i class="fas fa-hdd"></i> <span data-i18n="admin.storage_overview">Storage Overview</span></h2>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="ds-used">—</div>
|
||||
<div class="stat-label">Used</div>
|
||||
<div class="stat-label" data-i18n="admin.used">Used</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="ds-quota">—</div>
|
||||
<div class="stat-label">Total Quota</div>
|
||||
<div class="stat-label" data-i18n="admin.total_quota">Total Quota</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="ds-usage-pct">
|
||||
—
|
||||
</div>
|
||||
<div class="stat-label">Usage %</div>
|
||||
<div class="stat-label" data-i18n="admin.usage_pct">Usage %</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-bar">
|
||||
@@ -126,7 +127,7 @@
|
||||
>
|
||||
0
|
||||
</div>
|
||||
<div class="stat-label">
|
||||
<div class="stat-label" data-i18n="admin.users_over_80">
|
||||
Users >80% quota
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,7 +141,7 @@
|
||||
>
|
||||
0
|
||||
</div>
|
||||
<div class="stat-label">
|
||||
<div class="stat-label" data-i18n="admin.users_over_quota">
|
||||
Users over quota
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,21 +150,21 @@
|
||||
</div>
|
||||
|
||||
<div class="admin-card">
|
||||
<h2><i class="fas fa-server"></i> System</h2>
|
||||
<h2><i class="fas fa-server"></i> <span data-i18n="admin.system">System</span></h2>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="ds-auth">—</div>
|
||||
<div class="stat-label">Auth</div>
|
||||
<div class="stat-label" data-i18n="admin.auth_label">Auth</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="ds-oidc">—</div>
|
||||
<div class="stat-label">OIDC</div>
|
||||
<div class="stat-label" data-i18n="admin.oidc_label">OIDC</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="ds-quotas-flag">
|
||||
—
|
||||
</div>
|
||||
<div class="stat-label">Quotas</div>
|
||||
<div class="stat-label" data-i18n="admin.quotas_label">Quotas</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toggle-row toggle-row-strong">
|
||||
@@ -171,22 +172,19 @@
|
||||
><i
|
||||
class="fas fa-user-plus icon-muted-right"
|
||||
></i>
|
||||
Allow public self-registration</label
|
||||
<span data-i18n="admin.allow_registration">Allow public self-registration</span></label
|
||||
>
|
||||
<label class="switch"
|
||||
><input
|
||||
type="checkbox"
|
||||
id="ds-registration"
|
||||
checked
|
||||
onchange="toggleRegistration(this.checked)" /><span
|
||||
checked /><span
|
||||
class="slider"
|
||||
></span
|
||||
></label>
|
||||
</div>
|
||||
<div class="warning" id="registration-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> Public
|
||||
registration is disabled. Only admins can create new
|
||||
users.
|
||||
<i class="fas fa-exclamation-triangle"></i> <span data-i18n="admin.registration_warning">Public registration is disabled. Only admins can create new users.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -195,26 +193,25 @@
|
||||
<div class="admin-card">
|
||||
<h2 class="h2-space-between">
|
||||
<span
|
||||
><i class="fas fa-users-cog"></i> User
|
||||
Management</span
|
||||
><i class="fas fa-users-cog"></i> <span data-i18n="admin.user_management">User Management</span></span
|
||||
><button
|
||||
class="btn btn-primary"
|
||||
id="btn-create-user"
|
||||
>
|
||||
<i class="fas fa-user-plus"></i> Create User
|
||||
<i class="fas fa-user-plus"></i> <span data-i18n="admin.create_user">Create User</span>
|
||||
</button>
|
||||
</h2>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Role</th>
|
||||
<th>Auth</th>
|
||||
<th>Status</th>
|
||||
<th>Storage</th>
|
||||
<th>Last Login</th>
|
||||
<th>Actions</th>
|
||||
<th data-i18n="admin.col_user">User</th>
|
||||
<th data-i18n="admin.col_role">Role</th>
|
||||
<th data-i18n="admin.col_auth">Auth</th>
|
||||
<th data-i18n="admin.col_status">Status</th>
|
||||
<th data-i18n="admin.col_storage">Storage</th>
|
||||
<th data-i18n="admin.col_last_login">Last Login</th>
|
||||
<th data-i18n="admin.col_actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="users-tbody">
|
||||
@@ -226,7 +223,7 @@
|
||||
<i
|
||||
class="fas fa-spinner fa-spin"
|
||||
></i>
|
||||
Loading users…
|
||||
<span data-i18n="admin.loading_users">Loading users…</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -240,13 +237,13 @@
|
||||
id="prev-btn"
|
||||
disabled
|
||||
>
|
||||
<i class="fas fa-chevron-left"></i> Prev
|
||||
<i class="fas fa-chevron-left"></i> <span data-i18n="admin.prev">Prev</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-secondary"
|
||||
id="next-btn"
|
||||
>
|
||||
Next <i class="fas fa-chevron-right"></i>
|
||||
<span data-i18n="admin.next">Next</span> <i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -256,11 +253,10 @@
|
||||
<div id="tab-oidc" class="tab-content">
|
||||
<div class="admin-card">
|
||||
<h2>
|
||||
<i class="fas fa-shield-alt"></i> Single Sign-On
|
||||
(OIDC / SSO)
|
||||
<i class="fas fa-shield-alt"></i> <span data-i18n="admin.sso_title">Single Sign-On (OIDC / SSO)</span>
|
||||
</h2>
|
||||
<div class="toggle-row">
|
||||
<label>Enable SSO Authentication</label>
|
||||
<label data-i18n="admin.enable_sso">Enable SSO Authentication</label>
|
||||
<label class="switch"
|
||||
><input
|
||||
type="checkbox"
|
||||
@@ -272,7 +268,7 @@
|
||||
<div id="oidc-form">
|
||||
<div class="form-group">
|
||||
<label
|
||||
>Provider Name
|
||||
><span data-i18n="admin.provider_name">Provider Name</span>
|
||||
<span id="badge-provider_name"></span
|
||||
></label>
|
||||
<input
|
||||
@@ -283,7 +279,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
>Issuer URL
|
||||
><span data-i18n="admin.issuer_url">Issuer URL</span>
|
||||
<span id="badge-issuer_url"></span
|
||||
></label>
|
||||
<input
|
||||
@@ -291,7 +287,7 @@
|
||||
id="issuer-url"
|
||||
placeholder="https://auth.example.com/application/o/oxicloud/"
|
||||
/>
|
||||
<small
|
||||
<small data-i18n="admin.issuer_url_hint"
|
||||
>OpenID Connect issuer URL of your identity
|
||||
provider</small
|
||||
>
|
||||
@@ -301,13 +297,13 @@
|
||||
class="btn btn-secondary btn-sm"
|
||||
id="discover-btn"
|
||||
>
|
||||
<i class="fas fa-search"></i> Auto-discover
|
||||
<i class="fas fa-search"></i> <span data-i18n="admin.auto_discover">Auto-discover</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="discovery-result"></div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
>Client ID <span id="badge-client_id"></span
|
||||
><span data-i18n="admin.client_id">Client ID</span> <span id="badge-client_id"></span
|
||||
></label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -317,25 +313,26 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
>Client Secret
|
||||
><span data-i18n="admin.client_secret">Client Secret</span>
|
||||
<span id="badge-client_secret"></span
|
||||
></label>
|
||||
<input
|
||||
type="password"
|
||||
id="client-secret"
|
||||
placeholder="Leave empty to keep current value"
|
||||
data-i18n-placeholder="admin.client_secret_placeholder"
|
||||
/>
|
||||
<small id="secret-hint"
|
||||
><i
|
||||
class="fas fa-check-circle secret-icon"
|
||||
></i>
|
||||
A client secret is already configured</small
|
||||
<span data-i18n="admin.secret_configured">A client secret is already configured</span></small
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
>Callback URL
|
||||
<small class="small-muted"
|
||||
><span data-i18n="admin.callback_url">Callback URL</span>
|
||||
<small class="small-muted" data-i18n="admin.callback_url_hint"
|
||||
>(register in your IdP)</small
|
||||
></label
|
||||
>
|
||||
@@ -354,11 +351,11 @@
|
||||
<i
|
||||
class="fas fa-sliders-h summary-icon-right"
|
||||
></i>
|
||||
Advanced Settings
|
||||
<span data-i18n="admin.advanced_settings">Advanced Settings</span>
|
||||
</summary>
|
||||
<div class="form-group">
|
||||
<label
|
||||
>Scopes <span id="badge-scopes"></span
|
||||
><span data-i18n="admin.scopes">Scopes</span> <span id="badge-scopes"></span>
|
||||
></label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -367,7 +364,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="toggle-row">
|
||||
<label
|
||||
<label data-i18n="admin.auto_provision"
|
||||
>Auto-provision users on first
|
||||
login</label
|
||||
>
|
||||
@@ -382,21 +379,21 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
>Admin Groups
|
||||
<span id="badge-admin_groups"></span
|
||||
><span data-i18n="admin.admin_groups">Admin Groups</span>
|
||||
<span id="badge-admin_groups"></span>
|
||||
></label>
|
||||
<input
|
||||
type="text"
|
||||
id="admin-groups"
|
||||
placeholder="e.g., oxicloud-admins"
|
||||
/>
|
||||
<small
|
||||
<small data-i18n="admin.admin_groups_hint"
|
||||
>Comma-separated OIDC group names that
|
||||
map to admin role</small
|
||||
>
|
||||
</div>
|
||||
<div class="toggle-row">
|
||||
<label
|
||||
<label data-i18n="admin.disable_password"
|
||||
>Disable password login (OIDC
|
||||
only)</label
|
||||
>
|
||||
@@ -410,7 +407,7 @@
|
||||
</div>
|
||||
<div class="warning" id="password-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
This will prevent ALL password-based logins!
|
||||
<span data-i18n="admin.password_warning">This will prevent ALL password-based logins!</span>
|
||||
</div>
|
||||
</details>
|
||||
<div class="oidc-actions">
|
||||
@@ -418,13 +415,13 @@
|
||||
class="btn btn-secondary"
|
||||
id="btn-test-oidc"
|
||||
>
|
||||
<i class="fas fa-vial"></i> Test
|
||||
<i class="fas fa-vial"></i> <span data-i18n="admin.test_btn">Test</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
id="save-btn"
|
||||
>
|
||||
<i class="fas fa-save"></i> Save
|
||||
<i class="fas fa-save"></i> <span data-i18n="admin.save_btn">Save</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="oidc-status" class="alert"></div>
|
||||
@@ -437,14 +434,13 @@
|
||||
<div id="quota-modal" class="modal-overlay hidden">
|
||||
<div class="modal">
|
||||
<h3>
|
||||
<i class="fas fa-box modal-title-icon"></i> Update Storage
|
||||
Quota
|
||||
<i class="fas fa-box modal-title-icon"></i> <span data-i18n="admin.quota_modal_title">Update Storage Quota</span>
|
||||
</h3>
|
||||
<div class="form-group">
|
||||
<label>User: <strong id="qm-username"></strong></label>
|
||||
<label><span data-i18n="admin.quota_user_label">User:</span> <strong id="qm-username"></strong></label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>New Quota</label>
|
||||
<label data-i18n="admin.new_quota">New Quota</label>
|
||||
<div class="quota-input-row">
|
||||
<input
|
||||
type="number"
|
||||
@@ -459,17 +455,18 @@
|
||||
<option value="1099511627776">TB</option>
|
||||
</select>
|
||||
</div>
|
||||
<small>Set to 0 for unlimited</small>
|
||||
<small data-i18n="admin.quota_unlimited_hint">Set to 0 for unlimited</small>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
id="btn-close-quota"
|
||||
data-i18n="admin.cancel"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-primary" id="btn-save-quota">
|
||||
<i class="fas fa-save"></i> Save
|
||||
<i class="fas fa-save"></i> <span data-i18n="admin.save_btn">Save</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -478,50 +475,52 @@
|
||||
<div id="create-user-modal" class="modal-overlay hidden">
|
||||
<div class="modal">
|
||||
<h3>
|
||||
<i class="fas fa-user-plus modal-title-icon"></i> Create New
|
||||
User
|
||||
<i class="fas fa-user-plus modal-title-icon"></i> <span data-i18n="admin.create_user_title">Create New User</span>
|
||||
</h3>
|
||||
<div class="form-group">
|
||||
<label>Username *</label>
|
||||
<label><span data-i18n="admin.username_label">Username</span> *</label>
|
||||
<input
|
||||
type="text"
|
||||
id="cu-username"
|
||||
placeholder="johndoe"
|
||||
data-i18n-placeholder="admin.username_placeholder"
|
||||
minlength="3"
|
||||
maxlength="32"
|
||||
/>
|
||||
<small>3–32 characters</small>
|
||||
<small data-i18n="admin.username_hint">3–32 characters</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password *</label>
|
||||
<label><span data-i18n="admin.password_label">Password</span> *</label>
|
||||
<input
|
||||
type="password"
|
||||
id="cu-password"
|
||||
placeholder="Min 8 characters"
|
||||
data-i18n-placeholder="admin.password_placeholder"
|
||||
minlength="8"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
>Email
|
||||
<small class="small-muted">(optional)</small></label
|
||||
><span data-i18n="admin.email_label">Email</span>
|
||||
<small class="small-muted" data-i18n="admin.email_optional">(optional)</small></label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="cu-email"
|
||||
placeholder="user@example.com (auto-generated if empty)"
|
||||
data-i18n-placeholder="admin.email_placeholder"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group flex-1">
|
||||
<label>Role</label>
|
||||
<label data-i18n="admin.role_label">Role</label>
|
||||
<select id="cu-role" class="select-cu-role">
|
||||
<option value="user">User</option>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="user" data-i18n="admin.role_user">User</option>
|
||||
<option value="admin" data-i18n="admin.role_admin">Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group flex-1">
|
||||
<label>Quota</label>
|
||||
<label data-i18n="admin.quota_label">Quota</label>
|
||||
<div class="quota-row">
|
||||
<input
|
||||
type="number"
|
||||
@@ -547,6 +546,7 @@
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
id="btn-close-create-user"
|
||||
data-i18n="admin.cancel"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -554,7 +554,7 @@
|
||||
class="btn btn-primary"
|
||||
id="cu-submit"
|
||||
>
|
||||
<i class="fas fa-user-plus"></i> Create
|
||||
<i class="fas fa-user-plus"></i> <span data-i18n="admin.create_user">Create</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -563,13 +563,13 @@
|
||||
<div id="reset-pw-modal" class="modal-overlay hidden">
|
||||
<div class="modal">
|
||||
<h3>
|
||||
<i class="fas fa-key modal-title-icon"></i> Reset Password
|
||||
<i class="fas fa-key modal-title-icon"></i> <span data-i18n="admin.reset_pw_title">Reset Password</span>
|
||||
</h3>
|
||||
<div class="form-group">
|
||||
<label>User: <strong id="rp-username"></strong></label>
|
||||
<label><span data-i18n="admin.quota_user_label">User:</span> <strong id="rp-username"></strong></label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>New Password</label>
|
||||
<label data-i18n="admin.new_password_label">New Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="rp-password"
|
||||
@@ -582,6 +582,7 @@
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
id="btn-close-reset-pw"
|
||||
data-i18n="admin.cancel"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -589,12 +590,24 @@
|
||||
class="btn btn-primary"
|
||||
id="rp-submit"
|
||||
>
|
||||
<i class="fas fa-save"></i> Reset
|
||||
<i class="fas fa-save"></i> <span data-i18n="admin.reset_btn">Reset</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirm modal -->
|
||||
<div id="confirm-modal" class="modal-overlay hidden">
|
||||
<div class="modal modal-confirm">
|
||||
<h3 id="confirm-title"><i class="fas fa-exclamation-circle modal-title-icon"></i> <span data-i18n="admin.confirm_action">Confirm Action</span></h3>
|
||||
<p id="confirm-message"></p>
|
||||
<div class="modal-actions">
|
||||
<button class="btn btn-secondary" id="confirm-cancel" data-i18n="admin.confirm_no">Cancel</button>
|
||||
<button class="btn btn-danger" id="confirm-yes" data-i18n="admin.confirm_yes">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/views/admin/admin.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,9 +1,35 @@
|
||||
:root {
|
||||
/* Backgrounds */
|
||||
--color-bg-page: #f5f7fa;
|
||||
--color-bg-surface: #ffffff;
|
||||
--color-bg-input: #f9fafb;
|
||||
--color-bg-hover: #f8fafc;
|
||||
|
||||
/* Borders */
|
||||
--color-border: #e2e8f0;
|
||||
--color-border-light: #f1f5f9;
|
||||
|
||||
/* Text */
|
||||
--color-text: #2d3748;
|
||||
--color-text-heading: #1e293b;
|
||||
--color-text-muted: #718096;
|
||||
--color-text-faint: #94a3b8;
|
||||
|
||||
/* Accent */
|
||||
--color-accent: #ff5e3a;
|
||||
--color-accent-hover: #e04520;
|
||||
--color-accent-gradient: linear-gradient(135deg, #ff5e3a 0%, #ff2d55 100%);
|
||||
--color-accent-shadow: rgba(255, 94, 58, 0.3);
|
||||
--color-accent-ring: rgba(255, 94, 58, 0.1);
|
||||
--color-accent-tint: #fff5f3;
|
||||
|
||||
/* Feedback */
|
||||
--color-error-bg: #fee2e2;
|
||||
--color-error-text: #b91c1c;
|
||||
--color-success-bg: #dcfce7;
|
||||
--color-success-text: #15803d;
|
||||
|
||||
/* Shadows */
|
||||
--color-shadow: rgba(0, 0, 0, 0.1);
|
||||
--color-shadow-lg: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
top: calc(100% + 8px);
|
||||
right: 0;
|
||||
min-width: 160px;
|
||||
max-height: 420px;
|
||||
overflow-y: auto;
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
@@ -60,7 +62,22 @@
|
||||
transform: translateY(-10px);
|
||||
transition: all 0.2s ease;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
.language-selector-dropdown::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.language-selector-dropdown::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
margin: 8px 0;
|
||||
}
|
||||
.language-selector-dropdown::-webkit-scrollbar-thumb {
|
||||
background-color: #cbd5e0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.language-selector-dropdown::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #a0aec0;
|
||||
}
|
||||
|
||||
.language-selector.open .language-selector-dropdown {
|
||||
@@ -124,6 +141,13 @@
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .language-selector-dropdown::-webkit-scrollbar-thumb {
|
||||
background-color: #475569;
|
||||
}
|
||||
[data-theme="dark"] .language-selector-dropdown::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #64748b;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .language-option {
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,37 @@
|
||||
[data-theme="dark"] {
|
||||
/* Backgrounds */
|
||||
--color-bg-page: #0f172a;
|
||||
--color-bg-surface: #1e293b;
|
||||
--color-bg-input: #0f172a;
|
||||
--color-bg-hover: #162032;
|
||||
|
||||
/* Borders */
|
||||
--color-border: #334155;
|
||||
--color-border-light: #334155;
|
||||
|
||||
/* Text */
|
||||
--color-text: #e2e8f0;
|
||||
--color-text-heading: #f1f5f9;
|
||||
--color-text-muted: #94a3b8;
|
||||
--color-text-faint: #64748b;
|
||||
|
||||
/* Accent */
|
||||
--color-accent: #ff5e3a;
|
||||
--color-accent-hover: #ff7a5c;
|
||||
--color-accent-gradient: linear-gradient(135deg, #ff5e3a 0%, #ff2d55 100%);
|
||||
--color-accent-shadow: rgba(255, 94, 58, 0.3);
|
||||
--color-accent-ring: rgba(255, 94, 58, 0.15);
|
||||
--color-accent-tint: #2a1a15;
|
||||
|
||||
/* Feedback */
|
||||
--color-error-bg: #3b1111;
|
||||
--color-error-text: #fca5a5;
|
||||
--color-success-bg: #052e16;
|
||||
--color-success-text: #86efac;
|
||||
|
||||
/* Shadows */
|
||||
--color-shadow: rgba(0, 0, 0, 0.3);
|
||||
--color-shadow-lg: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
[data-theme="dark"] body {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
*{box-sizing:border-box;margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif}
|
||||
body{background:#f5f7fa;color:#1e293b;min-height:100vh;display:flex;flex-direction:column}
|
||||
body{background:#f5f7fa;color:#1e293b;min-height:100vh;height:auto;display:flex;flex-direction:column;overflow-y:auto;overflow-x:hidden}
|
||||
|
||||
.hidden{display:none !important}
|
||||
.show-block{display:block !important}
|
||||
@@ -62,10 +62,10 @@ body{background:#f5f7fa;color:#1e293b;min-height:100vh;display:flex;flex-directi
|
||||
.admin-header-right a:hover{color:#fff}
|
||||
|
||||
/* ── Container ── */
|
||||
.admin-container{max-width:1080px;margin:0 auto;padding:28px 24px 60px;width:100%}
|
||||
.admin-container{max-width:1080px;margin:0 auto;padding:28px 24px 60px;width:100%;flex:1}
|
||||
|
||||
/* ── Tabs ── */
|
||||
.admin-tabs{display:flex;gap:6px;margin-bottom:28px;background:#fff;border-radius:14px;padding:6px;box-shadow:0 1px 4px rgba(0,0,0,.06)}
|
||||
.admin-tabs{display:flex;gap:6px;margin-bottom:28px;background:#fff;border-radius:14px;padding:6px;box-shadow:0 1px 4px rgba(0,0,0,.06);position:sticky;top:64px;z-index:50}
|
||||
.admin-tab{
|
||||
padding:10px 22px;cursor:pointer;font-weight:600;font-size:13.5px;color:#64748b;
|
||||
border:none;background:none;border-radius:10px;transition:all .2s;display:flex;align-items:center;gap:8px;
|
||||
@@ -74,8 +74,12 @@ body{background:#f5f7fa;color:#1e293b;min-height:100vh;display:flex;flex-directi
|
||||
.admin-tab.active{color:#fff;background:linear-gradient(135deg,#ff5e3a,#ff2d55);box-shadow:0 3px 12px rgba(255,94,58,.25)}
|
||||
.admin-tab.active i{color:#fff}
|
||||
.admin-tab i{font-size:14px;width:16px;text-align:center}
|
||||
.tab-content{display:none}
|
||||
.tab-content{display:none;animation:none}
|
||||
.tab-content.active{display:block}
|
||||
.tab-content.tab-fade-in{animation:tabFadeIn .22s ease-out}
|
||||
.tab-content.tab-fade-out{animation:tabFadeOut .15s ease-in;display:block}
|
||||
@keyframes tabFadeIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
|
||||
@keyframes tabFadeOut{from{opacity:1}to{opacity:0}}
|
||||
|
||||
/* ── Cards ── */
|
||||
.admin-card{background:#fff;border-radius:16px;box-shadow:0 1px 4px rgba(0,0,0,.06),0 0 0 1px rgba(0,0,0,.03);padding:28px;margin-bottom:22px}
|
||||
@@ -296,6 +300,15 @@ details[open] summary{margin-bottom:14px;color:#ff5e3a}
|
||||
[data-theme="dark"] .pagination{color:#64748b}
|
||||
[data-theme="dark"] #access-denied h2{color:#fca5a5}
|
||||
[data-theme="dark"] #access-denied p{color:#94a3b8}
|
||||
|
||||
/* ── Confirm modal ── */
|
||||
.modal-confirm{max-width:420px}
|
||||
.modal-confirm h3{color:#dc2626}
|
||||
.modal-confirm p{margin:14px 0 22px;font-size:14px;line-height:1.55;color:#475569}
|
||||
.btn-danger{background:linear-gradient(135deg,#dc2626,#b91c1c);color:#fff;border:none;padding:10px 22px;border-radius:10px;font-weight:600;cursor:pointer;transition:opacity .15s}
|
||||
.btn-danger:hover{opacity:.88}
|
||||
[data-theme="dark"] .modal-confirm p{color:#94a3b8}
|
||||
[data-theme="dark"] .btn-danger{background:linear-gradient(135deg,#ef4444,#dc2626)}
|
||||
[data-theme="dark"] #access-denied .access-icon{background:#3b1111}
|
||||
[data-theme="dark"] #loading{color:#64748b}
|
||||
[data-theme="dark"] .toggle-row{border-top-color:#334155}
|
||||
|
||||
+125
-209
@@ -1,4 +1,7 @@
|
||||
/* Auth styles for OxiCloud */
|
||||
/* ============================================================
|
||||
Auth styles for OxiCloud — design tokens from variables.css
|
||||
============================================================ */
|
||||
|
||||
.auth-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -6,17 +9,18 @@
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
background-color: #f5f7fa;
|
||||
background-color: var(--color-bg-page);
|
||||
}
|
||||
|
||||
.auth-panel {
|
||||
width: 400px;
|
||||
width: 420px;
|
||||
max-width: 90%;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
|
||||
padding: 30px;
|
||||
background-color: var(--color-bg-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 30px var(--color-shadow);
|
||||
padding: 36px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -28,15 +32,15 @@
|
||||
}
|
||||
|
||||
.auth-logo-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: linear-gradient(135deg, #ff5e3a 0%, #ff2d55 100%);
|
||||
border-radius: 12px;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
background: var(--color-accent-gradient);
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 10px;
|
||||
box-shadow: 0 4px 12px rgba(255, 94, 58, 0.3);
|
||||
margin-right: 12px;
|
||||
box-shadow: 0 4px 12px var(--color-accent-shadow);
|
||||
}
|
||||
|
||||
.auth-logo-icon svg {
|
||||
@@ -48,14 +52,14 @@
|
||||
.auth-logo-text {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #2a3042;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.auth-title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 25px;
|
||||
color: #2a3042;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 28px;
|
||||
color: var(--color-text-heading);
|
||||
}
|
||||
|
||||
.auth-form {
|
||||
@@ -75,39 +79,55 @@
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
color: #4b5563;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-heading);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.auth-input {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
font-size: 14px;
|
||||
background-color: #f9fafb;
|
||||
transition: border-color 0.2s;
|
||||
padding: 14px 18px;
|
||||
border-radius: 12px;
|
||||
border: 2px solid var(--color-border);
|
||||
font-size: 15px;
|
||||
background-color: var(--color-bg-input);
|
||||
color: var(--color-text);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.auth-input::placeholder {
|
||||
color: var(--color-text-faint);
|
||||
}
|
||||
|
||||
.auth-input:hover {
|
||||
border-color: var(--color-accent);
|
||||
background-color: var(--color-bg-surface);
|
||||
}
|
||||
|
||||
.auth-input:focus {
|
||||
outline: none;
|
||||
border-color: #ff5e3a;
|
||||
box-shadow: 0 0 0 3px rgba(255, 94, 58, 0.1);
|
||||
border-color: var(--color-accent);
|
||||
background-color: var(--color-bg-surface);
|
||||
box-shadow: 0 0 0 3px var(--color-accent-ring);
|
||||
}
|
||||
|
||||
.auth-input[readonly] {
|
||||
opacity: 0.7;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.auth-button {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, #ff5e3a 0%, #ff2d55 100%);
|
||||
padding: 14px 18px;
|
||||
border-radius: 12px;
|
||||
background: var(--color-accent-gradient);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 10px;
|
||||
box-shadow: 0 4px 12px rgba(255, 94, 58, 0.3);
|
||||
margin-top: 12px;
|
||||
box-shadow: 0 4px 12px var(--color-accent-shadow);
|
||||
}
|
||||
|
||||
.auth-button:hover {
|
||||
@@ -118,15 +138,17 @@
|
||||
|
||||
.auth-button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 8px rgba(255, 94, 58, 0.3);
|
||||
box-shadow: 0 2px 8px var(--color-accent-shadow);
|
||||
}
|
||||
|
||||
.auth-button:disabled {
|
||||
background-color: #f9a799;
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.auth-subtitle { margin: 20px 0; color: #6b7280; font-size: 14px; }
|
||||
.auth-subtitle { margin: 20px 0; color: var(--color-text-muted); font-size: 14px; }
|
||||
.auth-action-wrap { margin-top: 20px; }
|
||||
|
||||
/* SSO / OIDC button */
|
||||
@@ -160,7 +182,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 20px 0;
|
||||
color: #a0aec0;
|
||||
color: var(--color-text-faint);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@@ -169,7 +191,7 @@
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: #e2e8f0;
|
||||
background: var(--color-border);
|
||||
}
|
||||
|
||||
.auth-divider span {
|
||||
@@ -182,13 +204,13 @@
|
||||
}
|
||||
|
||||
.auth-toggle {
|
||||
margin-top: 20px;
|
||||
margin-top: 22px;
|
||||
font-size: 14px;
|
||||
color: #718096;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.auth-toggle-link {
|
||||
color: #ff5e3a;
|
||||
color: var(--color-accent);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
@@ -199,20 +221,20 @@
|
||||
}
|
||||
|
||||
.auth-error {
|
||||
background-color: #fee2e2;
|
||||
color: #b91c1c;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-error-bg);
|
||||
color: var(--color-error-text);
|
||||
padding: 12px 18px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.auth-success {
|
||||
background-color: #dcfce7;
|
||||
color: #15803d;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-success-bg);
|
||||
color: var(--color-success-text);
|
||||
padding: 12px 18px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
@@ -224,44 +246,50 @@
|
||||
}
|
||||
|
||||
.setup-steps {
|
||||
margin-bottom: 25px;
|
||||
margin-bottom: 28px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.setup-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-color: #e2e8f0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
background-color: var(--color-bg-input);
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #64748b;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
color: var(--color-text-faint);
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.step-number.active {
|
||||
background-color: #ff5e3a;
|
||||
background: var(--color-accent-gradient);
|
||||
border-color: transparent;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px var(--color-accent-shadow);
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
color: var(--color-text-faint);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.step-title.active {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-heading);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Language selector panel styles */
|
||||
@@ -270,7 +298,7 @@
|
||||
}
|
||||
|
||||
.language-subtitle {
|
||||
color: #64748b;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
@@ -286,25 +314,25 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 14px 18px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
background-color: #f9fafb;
|
||||
background-color: var(--color-bg-input);
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.lang-picker-selected:hover {
|
||||
border-color: #ff5e3a;
|
||||
background-color: #fff;
|
||||
border-color: var(--color-accent);
|
||||
background-color: var(--color-bg-surface);
|
||||
}
|
||||
|
||||
.lang-picker.open .lang-picker-selected {
|
||||
border-color: #ff5e3a;
|
||||
background-color: #fff;
|
||||
border-color: var(--color-accent);
|
||||
background-color: var(--color-bg-surface);
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
box-shadow: 0 0 0 3px rgba(255, 94, 58, 0.1);
|
||||
box-shadow: 0 0 0 3px var(--color-accent-ring);
|
||||
}
|
||||
|
||||
.lang-picker-flag {
|
||||
@@ -316,12 +344,12 @@
|
||||
.lang-picker-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
color: var(--color-text-heading);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.lang-picker-arrow {
|
||||
color: #94a3b8;
|
||||
color: var(--color-text-faint);
|
||||
font-size: 13px;
|
||||
transition: transform 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
@@ -338,12 +366,12 @@
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
border: 2px solid #ff5e3a;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
background: var(--color-bg-surface);
|
||||
border: 2px solid var(--color-accent);
|
||||
border-top: 1px solid var(--color-border-light);
|
||||
border-bottom-left-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
|
||||
box-shadow: 0 12px 32px var(--color-shadow-lg);
|
||||
z-index: 100;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -362,7 +390,7 @@
|
||||
.lang-picker-search {
|
||||
position: relative;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
border-bottom: 1px solid var(--color-border-light);
|
||||
}
|
||||
|
||||
.lang-picker-search i {
|
||||
@@ -370,23 +398,29 @@
|
||||
left: 26px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #94a3b8;
|
||||
color: var(--color-text-faint);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.lang-picker-search input {
|
||||
width: 100%;
|
||||
padding: 8px 12px 8px 32px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--color-bg-input);
|
||||
color: var(--color-text);
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.lang-picker-search input::placeholder {
|
||||
color: var(--color-text-faint);
|
||||
}
|
||||
|
||||
.lang-picker-search input:focus {
|
||||
border-color: #ff5e3a;
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
|
||||
/* Scrollable list */
|
||||
@@ -405,12 +439,12 @@
|
||||
}
|
||||
|
||||
.lang-picker-list::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
background: var(--color-border);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.lang-picker-list::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
background: var(--color-text-faint);
|
||||
}
|
||||
|
||||
/* Language item in dropdown */
|
||||
@@ -425,11 +459,11 @@
|
||||
}
|
||||
|
||||
.lang-picker-item:hover {
|
||||
background: #f8fafc;
|
||||
background: var(--color-bg-hover);
|
||||
}
|
||||
|
||||
.lang-picker-item.selected {
|
||||
background: #fff5f3;
|
||||
background: var(--color-accent-tint);
|
||||
}
|
||||
|
||||
.lang-picker-item-flag {
|
||||
@@ -440,32 +474,32 @@
|
||||
.lang-picker-item-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
color: var(--color-text-heading);
|
||||
}
|
||||
|
||||
.lang-picker-item-english {
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
color: var(--color-text-faint);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.lang-picker-item-check {
|
||||
color: #ff5e3a;
|
||||
color: var(--color-accent);
|
||||
font-size: 13px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.lang-picker-empty {
|
||||
text-align: center;
|
||||
color: #94a3b8;
|
||||
color: var(--color-text-faint);
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.auth-panel {
|
||||
width: 90%;
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.lang-picker-selected {
|
||||
@@ -476,121 +510,3 @@
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
DARK MODE — Auth Pages
|
||||
============================================================ */
|
||||
[data-theme="dark"] .auth-container {
|
||||
background-color: #0f172a;
|
||||
}
|
||||
[data-theme="dark"] .auth-panel {
|
||||
background-color: #1e293b;
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
[data-theme="dark"] .auth-logo-text {
|
||||
color: #f1f5f9;
|
||||
}
|
||||
[data-theme="dark"] .auth-title {
|
||||
color: #f1f5f9;
|
||||
}
|
||||
[data-theme="dark"] .auth-label {
|
||||
color: #94a3b8;
|
||||
}
|
||||
[data-theme="dark"] .auth-input {
|
||||
background-color: #0f172a;
|
||||
border-color: #334155;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
[data-theme="dark"] .auth-input:focus {
|
||||
border-color: #ff5e3a;
|
||||
background-color: #0f172a;
|
||||
box-shadow: 0 0 0 3px rgba(255, 94, 58, 0.15);
|
||||
}
|
||||
[data-theme="dark"] .auth-input::placeholder {
|
||||
color: #64748b;
|
||||
}
|
||||
[data-theme="dark"] .auth-error {
|
||||
background-color: #3b1111;
|
||||
color: #fca5a5;
|
||||
}
|
||||
[data-theme="dark"] .auth-success {
|
||||
background-color: #052e16;
|
||||
color: #86efac;
|
||||
}
|
||||
[data-theme="dark"] .auth-toggle {
|
||||
color: #94a3b8;
|
||||
}
|
||||
[data-theme="dark"] .auth-divider {
|
||||
color: #64748b;
|
||||
}
|
||||
[data-theme="dark"] .auth-divider::before,
|
||||
[data-theme="dark"] .auth-divider::after {
|
||||
background: #334155;
|
||||
}
|
||||
[data-theme="dark"] .language-subtitle {
|
||||
color: #94a3b8;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-selected {
|
||||
background-color: #0f172a;
|
||||
border-color: #334155;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-selected:hover {
|
||||
border-color: #ff5e3a;
|
||||
background-color: #162032;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker.open .lang-picker-selected {
|
||||
border-color: #ff5e3a;
|
||||
background-color: #162032;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-name {
|
||||
color: #f1f5f9;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-arrow {
|
||||
color: #64748b;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-dropdown {
|
||||
background: #1e293b;
|
||||
border-color: #ff5e3a;
|
||||
border-top-color: #334155;
|
||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-search {
|
||||
border-bottom-color: #334155;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-search input {
|
||||
background-color: #0f172a;
|
||||
border-color: #334155;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-search input:focus {
|
||||
border-color: #ff5e3a;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-search input::placeholder {
|
||||
color: #64748b;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-list::-webkit-scrollbar-thumb {
|
||||
background: #475569;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-item:hover {
|
||||
background: #162032;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-item.selected {
|
||||
background: #2a1a15;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-item-name {
|
||||
color: #f1f5f9;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-item-english {
|
||||
color: #64748b;
|
||||
}
|
||||
[data-theme="dark"] .lang-picker-empty {
|
||||
color: #64748b;
|
||||
}
|
||||
/* Setup steps */
|
||||
[data-theme="dark"] .step-number {
|
||||
background-color: #334155;
|
||||
color: #94a3b8;
|
||||
}
|
||||
[data-theme="dark"] .step-title {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
@@ -110,8 +110,19 @@ async function loadFiles(options = {}) {
|
||||
const folderList = Array.isArray(listing.folders) ? listing.folders : [];
|
||||
const fileList = Array.isArray(listing.files) ? listing.files : [];
|
||||
|
||||
if (folderList.length === 0 && fileList.length === 0) {
|
||||
const emptyState = document.createElement('div');
|
||||
emptyState.className = 'empty-state';
|
||||
emptyState.innerHTML = `
|
||||
<i class="fas fa-folder-open empty-state-icon"></i>
|
||||
<p>${_t('files.no_files')}</p>
|
||||
<p>${_t('files.empty_hint')}</p>
|
||||
`;
|
||||
elements.filesGrid.appendChild(emptyState);
|
||||
} else {
|
||||
window.ui.renderFolders(folderList);
|
||||
window.ui.renderFiles(fileList);
|
||||
}
|
||||
|
||||
console.log(`Loaded ${folderList.length} folders and ${fileList.length} files`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -473,8 +473,8 @@ function setupEventListeners() {
|
||||
// Show files containers (to be filled with trash)
|
||||
const filesGrid = document.getElementById('files-grid');
|
||||
const filesListView = document.getElementById('files-list-view');
|
||||
if (filesGrid) filesGrid.style.display = app.currentView === 'grid' ? 'grid' : 'none';
|
||||
if (filesListView) filesListView.style.display = app.currentView === 'list' ? 'block' : 'none';
|
||||
if (filesGrid) { filesGrid.style.display = app.currentView === 'grid' ? 'grid' : 'none'; filesGrid.classList.toggle('hidden', app.currentView !== 'grid'); }
|
||||
if (filesListView) { filesListView.style.display = app.currentView === 'list' ? 'flex' : 'none'; filesListView.classList.toggle('hidden', app.currentView !== 'list'); }
|
||||
|
||||
// Update UI
|
||||
elements.pageTitle.textContent = window.i18n ? window.i18n.t('nav.trash') : 'Trash';
|
||||
@@ -494,6 +494,8 @@ function setupEventListeners() {
|
||||
const savedView = localStorage.getItem('oxicloud-view');
|
||||
if (savedView === 'list') {
|
||||
ui.switchToListView();
|
||||
} else {
|
||||
ui.switchToGridView();
|
||||
}
|
||||
|
||||
// User menu
|
||||
|
||||
@@ -3,6 +3,34 @@
|
||||
* Extracted from main.js to keep navigation concerns isolated.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sync the hidden class and inline display for the grid/list containers
|
||||
* based on the current view preference.
|
||||
*/
|
||||
function syncViewContainers() {
|
||||
const filesGrid = document.getElementById('files-grid');
|
||||
const filesListView = document.getElementById('files-list-view');
|
||||
const isGrid = window.app.currentView === 'grid';
|
||||
if (filesGrid) {
|
||||
filesGrid.style.display = isGrid ? 'grid' : 'none';
|
||||
filesGrid.classList.toggle('hidden', !isGrid);
|
||||
}
|
||||
if (filesListView) {
|
||||
filesListView.style.display = isGrid ? 'none' : 'flex';
|
||||
filesListView.classList.toggle('hidden', isGrid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide both grid and list containers (used when switching to non-file views).
|
||||
*/
|
||||
function hideFileContainers() {
|
||||
const filesGrid = document.getElementById('files-grid');
|
||||
const filesListView = document.getElementById('files-list-view');
|
||||
if (filesGrid) { filesGrid.style.display = 'none'; filesGrid.classList.add('hidden'); }
|
||||
if (filesListView) { filesListView.style.display = 'none'; filesListView.classList.add('hidden'); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Mobile sidebar toggle functionality
|
||||
*/
|
||||
@@ -136,6 +164,8 @@ function switchToSharedView() {
|
||||
const filesListView = document.getElementById('files-list-view');
|
||||
if (filesGrid) filesGrid.style.display = 'none';
|
||||
if (filesListView) filesListView.style.display = 'none';
|
||||
if (filesGrid) filesGrid.classList.add('hidden');
|
||||
if (filesListView) filesListView.classList.add('hidden');
|
||||
|
||||
// Show shared view
|
||||
if (window.sharedView) {
|
||||
@@ -157,7 +187,9 @@ function switchToFilesView() {
|
||||
const filesGrid = document.getElementById('files-grid');
|
||||
const filesListView = document.getElementById('files-list-view');
|
||||
if (filesGrid) filesGrid.style.display = window.app.currentView === 'grid' ? 'grid' : 'none';
|
||||
if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'block' : 'none';
|
||||
if (filesGrid) filesGrid.classList.toggle('hidden', window.app.currentView !== 'grid');
|
||||
if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'flex' : 'none';
|
||||
if (filesListView) filesListView.classList.toggle('hidden', window.app.currentView !== 'list');
|
||||
|
||||
// Reset to home folder and update breadcrumb
|
||||
window.app.currentPath = window.app.userHomeFolderId || '';
|
||||
@@ -180,7 +212,9 @@ function switchToFavoritesView() {
|
||||
const filesGrid = document.getElementById('files-grid');
|
||||
const filesListView = document.getElementById('files-list-view');
|
||||
if (filesGrid) filesGrid.style.display = window.app.currentView === 'grid' ? 'grid' : 'none';
|
||||
if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'block' : 'none';
|
||||
if (filesGrid) filesGrid.classList.toggle('hidden', window.app.currentView !== 'grid');
|
||||
if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'flex' : 'none';
|
||||
if (filesListView) filesListView.classList.toggle('hidden', window.app.currentView !== 'list');
|
||||
|
||||
if (window.favorites) {
|
||||
window.favorites.displayFavorites();
|
||||
@@ -211,7 +245,9 @@ function switchToRecentFilesView() {
|
||||
const filesGrid = document.getElementById('files-grid');
|
||||
const filesListView = document.getElementById('files-list-view');
|
||||
if (filesGrid) filesGrid.style.display = window.app.currentView === 'grid' ? 'grid' : 'none';
|
||||
if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'block' : 'none';
|
||||
if (filesGrid) filesGrid.classList.toggle('hidden', window.app.currentView !== 'grid');
|
||||
if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'flex' : 'none';
|
||||
if (filesListView) filesListView.classList.toggle('hidden', window.app.currentView !== 'list');
|
||||
|
||||
if (window.recent) {
|
||||
window.recent.displayRecentFiles();
|
||||
@@ -242,8 +278,8 @@ function switchToPhotosView() {
|
||||
// Hide file containers
|
||||
const filesGrid = document.getElementById('files-grid');
|
||||
const filesListView = document.getElementById('files-list-view');
|
||||
if (filesGrid) filesGrid.style.display = 'none';
|
||||
if (filesListView) filesListView.style.display = 'none';
|
||||
if (filesGrid) { filesGrid.style.display = 'none'; filesGrid.classList.add('hidden'); }
|
||||
if (filesListView) { filesListView.style.display = 'none'; filesListView.classList.add('hidden'); }
|
||||
|
||||
// Show photos view
|
||||
if (window.photosView) {
|
||||
|
||||
@@ -450,7 +450,9 @@ const ui = {
|
||||
this._hydrateViewIfNeeded('grid');
|
||||
|
||||
filesGrid.style.display = 'grid';
|
||||
filesGrid.classList.remove('hidden');
|
||||
filesListView.style.display = 'none';
|
||||
filesListView.classList.add('hidden');
|
||||
gridViewBtn.classList.add('active');
|
||||
listViewBtn.classList.remove('active');
|
||||
window.app.currentView = 'grid';
|
||||
@@ -469,7 +471,9 @@ const ui = {
|
||||
this._hydrateViewIfNeeded('list');
|
||||
|
||||
filesGrid.style.display = 'none';
|
||||
filesGrid.classList.add('hidden');
|
||||
filesListView.style.display = 'flex';
|
||||
filesListView.classList.remove('hidden');
|
||||
gridViewBtn.classList.remove('active');
|
||||
listViewBtn.classList.add('active');
|
||||
window.app.currentView = 'list';
|
||||
|
||||
+26
-10
@@ -13,7 +13,7 @@ let currentLocale =
|
||||
|
||||
// Supported locales (languages that have locale files on the server)
|
||||
// When a locale file is not found, the system gracefully falls back to English
|
||||
const supportedLocales = ['en', 'es', 'zh', 'fa', 'fr', 'de', 'pt', 'nl'];
|
||||
const supportedLocales = ['en', 'es', 'zh', 'fa', 'fr', 'de', 'pt', 'nl', 'it', 'hi', 'ar', 'ru', 'ja', 'ko'];
|
||||
|
||||
// Fallback to English if locale is not supported
|
||||
if (!supportedLocales.includes(currentLocale)) {
|
||||
@@ -226,6 +226,9 @@ async function initI18n() {
|
||||
await loadTranslations('en');
|
||||
}
|
||||
|
||||
// Mark loaded BEFORE translatePage so safeT resolves properly
|
||||
translationsLoaded = true;
|
||||
|
||||
// Translate the page
|
||||
translatePage();
|
||||
|
||||
@@ -245,20 +248,23 @@ function translatePage() {
|
||||
* @param {Element|Document} root - The root element to search within
|
||||
*/
|
||||
function translateElement(root) {
|
||||
// Use safeT instead of bare t() to avoid issues when other scripts
|
||||
// (e.g. admin.js) shadow the global t() function.
|
||||
const resolve = safeT;
|
||||
const el = root || document;
|
||||
el.querySelectorAll('[data-i18n]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n');
|
||||
element.textContent = t(key);
|
||||
element.textContent = resolve(key);
|
||||
});
|
||||
|
||||
el.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-placeholder');
|
||||
element.placeholder = t(key);
|
||||
element.placeholder = resolve(key);
|
||||
});
|
||||
|
||||
el.querySelectorAll('[data-i18n-title]').forEach(element => {
|
||||
const key = element.getAttribute('data-i18n-title');
|
||||
element.title = t(key);
|
||||
element.title = resolve(key);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -284,19 +290,29 @@ let translationsLoaded = false;
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await initI18n();
|
||||
translationsLoaded = true;
|
||||
// translationsLoaded already set inside initI18n
|
||||
// Dispatch an event when translations are fully loaded
|
||||
window.dispatchEvent(new Event('translationsLoaded'));
|
||||
});
|
||||
|
||||
// Improved t function with fallback for early calls
|
||||
// Self-contained t wrapper — does NOT call the global t() because other
|
||||
// scripts (e.g. admin.js) may shadow it, which would cause infinite recursion.
|
||||
function safeT(key, params = {}) {
|
||||
if (!translationsLoaded) {
|
||||
console.warn(`Translations for ${currentLocale} not loaded yet`);
|
||||
// Return a default value or the key depending on context
|
||||
const localeData = translations[currentLocale];
|
||||
if (!localeData) {
|
||||
// Translations not loaded yet — return humanised key suffix
|
||||
return key.split('.').pop() || key;
|
||||
}
|
||||
return t(key, params);
|
||||
|
||||
let value = getNestedValue(localeData, key);
|
||||
|
||||
// Fallback to English
|
||||
if (!value && currentLocale !== 'en' && translations['en']) {
|
||||
value = getNestedValue(translations['en'], key);
|
||||
}
|
||||
|
||||
if (!value) return key;
|
||||
return interpolate(value, params);
|
||||
}
|
||||
|
||||
// Export functions for use in other modules
|
||||
|
||||
@@ -3,11 +3,20 @@
|
||||
* Custom styled dropdown with flags
|
||||
*/
|
||||
|
||||
// Locale files that actually exist (have full translations)
|
||||
// Keep this list in sync when adding new locale JSON files
|
||||
const AVAILABLE_LOCALES = new Set([
|
||||
'en', 'es', 'zh', 'fa', 'fr', 'de', 'pt', 'it', 'nl',
|
||||
'hi', 'ar', 'ru', 'ja', 'ko'
|
||||
]);
|
||||
|
||||
// Language codes, names, and flag emojis
|
||||
// Uses ALL_LANGUAGES from auth.js if available, otherwise fallback
|
||||
// Only returns languages that have a real locale file
|
||||
function getAvailableLanguages() {
|
||||
if (typeof ALL_LANGUAGES !== 'undefined') {
|
||||
return ALL_LANGUAGES.map(l => ({ code: l.code, name: l.nativeName, flag: l.flag }));
|
||||
return ALL_LANGUAGES
|
||||
.filter(l => AVAILABLE_LOCALES.has(l.code))
|
||||
.map(l => ({ code: l.code, name: l.nativeName, flag: l.flag }));
|
||||
}
|
||||
return [
|
||||
{ code: 'en', name: 'English', flag: '🇬🇧' },
|
||||
@@ -23,7 +32,7 @@ function getAvailableLanguages() {
|
||||
}
|
||||
|
||||
// RTL languages
|
||||
const rtlLanguages = ['fa']; // ['fa', 'ar']
|
||||
const rtlLanguages = ['fa', 'ar'];
|
||||
|
||||
// Update HTML lang attribute and dir for RTL languages
|
||||
function updateHtmlAttributes(langCode) {
|
||||
|
||||
@@ -19,8 +19,8 @@ const FIRST_RUN_KEY = 'oxicloud_first_run_completed';
|
||||
// Language selector texts (used before i18n is loaded)
|
||||
const LANGUAGE_TEXTS = {
|
||||
en: {
|
||||
title: 'Welcome to OxiCloud',
|
||||
subtitle: 'Please select your language',
|
||||
title: 'Welcome!',
|
||||
subtitle: 'Select your language to continue',
|
||||
continue: 'Continue',
|
||||
autodetected: 'We detected your language',
|
||||
moreLanguages: 'More languages...',
|
||||
@@ -28,8 +28,8 @@ const LANGUAGE_TEXTS = {
|
||||
searchPlaceholder: 'Search language...'
|
||||
},
|
||||
es: {
|
||||
title: 'Bienvenido a OxiCloud',
|
||||
subtitle: 'Por favor, selecciona tu idioma',
|
||||
title: '¡Bienvenido!',
|
||||
subtitle: 'Selecciona tu idioma para continuar',
|
||||
continue: 'Continuar',
|
||||
autodetected: 'Hemos detectado tu idioma',
|
||||
moreLanguages: 'More languages...',
|
||||
@@ -37,8 +37,8 @@ const LANGUAGE_TEXTS = {
|
||||
searchPlaceholder: 'Buscar idioma...'
|
||||
},
|
||||
zh: {
|
||||
title: '欢迎使用 OxiCloud',
|
||||
subtitle: '请选择您的语言',
|
||||
title: '欢迎!',
|
||||
subtitle: '选择您的语言以继续',
|
||||
continue: '继续',
|
||||
autodetected: '我们检测到了您的语言',
|
||||
moreLanguages: '更多语言...',
|
||||
@@ -46,8 +46,8 @@ const LANGUAGE_TEXTS = {
|
||||
searchPlaceholder: '搜索语言...'
|
||||
},
|
||||
fa: {
|
||||
title: 'به OxiCloud خوش آمدید',
|
||||
subtitle: 'لطفا زبان خود را انتخاب کنید',
|
||||
title: '!خوش آمدید',
|
||||
subtitle: 'زبان خود را برای ادامه انتخاب کنید',
|
||||
continue: 'ادامه',
|
||||
autodetected: 'زبان شما شناسایی شد',
|
||||
moreLanguages: 'زبانهای بیشتر...',
|
||||
@@ -55,14 +55,59 @@ const LANGUAGE_TEXTS = {
|
||||
searchPlaceholder: 'جستجوی زبان...'
|
||||
},
|
||||
nl: {
|
||||
title: 'Welkom bij OxiCloud',
|
||||
subtitle: 'Selecteer uw taal',
|
||||
title: 'Welkom!',
|
||||
subtitle: 'Selecteer uw taal om door te gaan',
|
||||
continue: 'Doorgaan',
|
||||
autodetected: 'We hebben uw taal gedetecteerd',
|
||||
moreLanguages: 'Meer talen...',
|
||||
modalTitle: 'Taal selecteren',
|
||||
searchPlaceholder: 'Zoek taal...'
|
||||
},
|
||||
hi: {
|
||||
title: 'स्वागत है!',
|
||||
subtitle: 'जारी रखने के लिए अपनी भाषा चुनें',
|
||||
continue: 'जारी रखें',
|
||||
autodetected: 'हमने आपकी भाषा पहचान ली',
|
||||
moreLanguages: 'और भाषाएँ...',
|
||||
modalTitle: 'भाषा चुनें',
|
||||
searchPlaceholder: 'भाषा खोजें...'
|
||||
},
|
||||
ar: {
|
||||
title: '!مرحباً',
|
||||
subtitle: 'اختر لغتك للمتابعة',
|
||||
continue: 'متابعة',
|
||||
autodetected: 'تم اكتشاف لغتك',
|
||||
moreLanguages: 'المزيد من اللغات...',
|
||||
modalTitle: 'اختر اللغة',
|
||||
searchPlaceholder: 'ابحث عن لغة...'
|
||||
},
|
||||
ru: {
|
||||
title: 'Добро пожаловать!',
|
||||
subtitle: 'Выберите язык для продолжения',
|
||||
continue: 'Продолжить',
|
||||
autodetected: 'Мы определили ваш язык',
|
||||
moreLanguages: 'Больше языков...',
|
||||
modalTitle: 'Выберите язык',
|
||||
searchPlaceholder: 'Поиск языка...'
|
||||
},
|
||||
ja: {
|
||||
title: 'ようこそ!',
|
||||
subtitle: '続行するには言語を選択してください',
|
||||
continue: '続行',
|
||||
autodetected: '言語を検出しました',
|
||||
moreLanguages: 'その他の言語...',
|
||||
modalTitle: '言語を選択',
|
||||
searchPlaceholder: '言語を検索...'
|
||||
},
|
||||
ko: {
|
||||
title: '환영합니다!',
|
||||
subtitle: '계속하려면 언어를 선택하세요',
|
||||
continue: '계속',
|
||||
autodetected: '언어가 감지되었습니다',
|
||||
moreLanguages: '더 많은 언어...',
|
||||
modalTitle: '언어 선택',
|
||||
searchPlaceholder: '언어 검색...'
|
||||
},
|
||||
};
|
||||
|
||||
// Complete language registry — add new languages here, they'll appear automatically
|
||||
@@ -76,11 +121,11 @@ const ALL_LANGUAGES = [
|
||||
{ code: 'de', name: 'German', nativeName: 'Deutsch', flag: '🇩🇪', popular: true },
|
||||
{ code: 'pt', name: 'Portuguese', nativeName: 'Português', flag: '🇧🇷', popular: true },
|
||||
{ code: 'it', name: 'Italian', nativeName: 'Italiano', flag: '🇮🇹', popular: true },
|
||||
{ code: 'ru', name: 'Russian', nativeName: 'Русский', flag: '🇷🇺', popular: false },
|
||||
{ code: 'ja', name: 'Japanese', nativeName: '日本語', flag: '🇯🇵', popular: false },
|
||||
{ code: 'ko', name: 'Korean', nativeName: '한국어', flag: '🇰🇷', popular: false },
|
||||
{ code: 'ar', name: 'Arabic', nativeName: 'العربية', flag: '🇸🇦', popular: false },
|
||||
{ code: 'hi', name: 'Hindi', nativeName: 'हिन्दी', flag: '🇮🇳', popular: false },
|
||||
{ code: 'ru', name: 'Russian', nativeName: 'Русский', flag: '🇷🇺', popular: true },
|
||||
{ code: 'ja', name: 'Japanese', nativeName: '日本語', flag: '🇯🇵', popular: true },
|
||||
{ code: 'ko', name: 'Korean', nativeName: '한국어', flag: '🇰🇷', popular: true },
|
||||
{ code: 'ar', name: 'Arabic', nativeName: 'العربية', flag: '🇸🇦', popular: true },
|
||||
{ code: 'hi', name: 'Hindi', nativeName: 'हिन्दी', flag: '🇮🇳', popular: true },
|
||||
{ code: 'tr', name: 'Turkish', nativeName: 'Türkçe', flag: '🇹🇷', popular: false },
|
||||
{ code: 'nl', name: 'Dutch', nativeName: 'Nederlands', flag: '🇳🇱', popular: false },
|
||||
{ code: 'pl', name: 'Polish', nativeName: 'Polski', flag: '🇵🇱', popular: false },
|
||||
|
||||
+143
-57
@@ -4,8 +4,14 @@ let usersPage = 0;
|
||||
const PAGE_SIZE = 50;
|
||||
let totalUsers = 0;
|
||||
|
||||
/** Escape a string for safe embedding inside a JS string literal within an HTML attribute.
|
||||
* Converts all non-alphanumeric/space/dot/hyphen/underscore chars to \xHH escapes. */
|
||||
/* ── i18n helper — falls back to key if i18n not ready ── */
|
||||
function t(key, params) {
|
||||
if (window.i18n && typeof window.i18n.t === 'function') return window.i18n.t(key, params);
|
||||
// fallback: strip prefix and humanise
|
||||
return key.split('.').pop().replace(/_/g, ' ');
|
||||
}
|
||||
|
||||
/** Escape a string for safe embedding inside a JS string literal within an HTML attribute. */
|
||||
function _escJs(s) {
|
||||
if (typeof s !== 'string') return '';
|
||||
return s.replace(/[^\w .\-]/g, function(c) {
|
||||
@@ -43,22 +49,80 @@ function formatBytes(bytes) {
|
||||
}
|
||||
|
||||
function timeAgo(dateStr) {
|
||||
if (!dateStr) return 'Never';
|
||||
if (!dateStr) return t('admin.never');
|
||||
const d = new Date(dateStr);
|
||||
const now = new Date();
|
||||
const secs = Math.floor((now - d) / 1000);
|
||||
if (secs < 60) return 'Just now';
|
||||
if (secs < 3600) return Math.floor(secs/60) + 'm ago';
|
||||
if (secs < 86400) return Math.floor(secs/3600) + 'h ago';
|
||||
if (secs < 2592000) return Math.floor(secs/86400) + 'd ago';
|
||||
if (secs < 60) return t('admin.just_now');
|
||||
if (secs < 3600) return t('admin.minutes_ago', { n: Math.floor(secs / 60) });
|
||||
if (secs < 86400) return t('admin.hours_ago', { n: Math.floor(secs / 3600) });
|
||||
if (secs < 2592000) return t('admin.days_ago', { n: Math.floor(secs / 86400) });
|
||||
return d.toLocaleDateString();
|
||||
}
|
||||
|
||||
/* ── Custom confirm modal ── */
|
||||
function showConfirm(message) {
|
||||
return new Promise(function(resolve) {
|
||||
var overlay = document.getElementById('confirm-modal');
|
||||
var msgEl = document.getElementById('confirm-message');
|
||||
var yesBtn = document.getElementById('confirm-yes');
|
||||
var noBtn = document.getElementById('confirm-cancel');
|
||||
msgEl.textContent = message;
|
||||
overlay.classList.remove('hidden');
|
||||
overlay.classList.add('show-flex');
|
||||
|
||||
function cleanup(result) {
|
||||
overlay.classList.remove('show-flex');
|
||||
overlay.classList.add('hidden');
|
||||
yesBtn.removeEventListener('click', onYes);
|
||||
noBtn.removeEventListener('click', onNo);
|
||||
overlay.removeEventListener('click', onOverlay);
|
||||
resolve(result);
|
||||
}
|
||||
function onYes() { cleanup(true); }
|
||||
function onNo() { cleanup(false); }
|
||||
function onOverlay(e) { if (e.target === overlay) cleanup(false); }
|
||||
yesBtn.addEventListener('click', onYes);
|
||||
noBtn.addEventListener('click', onNo);
|
||||
overlay.addEventListener('click', onOverlay);
|
||||
});
|
||||
}
|
||||
|
||||
/* ── Tab switching with fade animation ── */
|
||||
let activeTabName = 'dashboard';
|
||||
|
||||
function switchTab(name, el) {
|
||||
document.querySelectorAll('.admin-tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
|
||||
document.getElementById('tab-' + name).classList.add('active');
|
||||
if (name === activeTabName) return;
|
||||
var oldTab = document.getElementById('tab-' + activeTabName);
|
||||
var newTab = document.getElementById('tab-' + name);
|
||||
|
||||
document.querySelectorAll('.admin-tab').forEach(function(b) { b.classList.remove('active'); });
|
||||
if (el) el.classList.add('active');
|
||||
|
||||
// Fade-out old tab
|
||||
if (oldTab) {
|
||||
oldTab.classList.add('tab-fade-out');
|
||||
oldTab.addEventListener('animationend', function handler() {
|
||||
oldTab.removeEventListener('animationend', handler);
|
||||
oldTab.classList.remove('active', 'tab-fade-out');
|
||||
// Fade-in new tab
|
||||
if (newTab) {
|
||||
newTab.classList.add('active', 'tab-fade-in');
|
||||
newTab.addEventListener('animationend', function handler2() {
|
||||
newTab.removeEventListener('animationend', handler2);
|
||||
newTab.classList.remove('tab-fade-in');
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (newTab) {
|
||||
newTab.classList.add('active', 'tab-fade-in');
|
||||
newTab.addEventListener('animationend', function handler2() {
|
||||
newTab.removeEventListener('animationend', handler2);
|
||||
newTab.classList.remove('tab-fade-in');
|
||||
});
|
||||
}
|
||||
|
||||
activeTabName = name;
|
||||
if (name === 'users') loadUsers();
|
||||
if (name === 'dashboard') loadDashboard();
|
||||
}
|
||||
@@ -78,9 +142,9 @@ async function loadDashboard() {
|
||||
const bar = document.getElementById('ds-bar');
|
||||
bar.style.width = Math.min(d.storage_usage_percent, 100) + '%';
|
||||
bar.className = 'progress-fill ' + (d.storage_usage_percent > 90 ? 'red' : d.storage_usage_percent > 70 ? 'orange' : 'green');
|
||||
document.getElementById('ds-auth').textContent = d.auth_enabled ? 'Enabled' : 'Disabled';
|
||||
document.getElementById('ds-oidc').textContent = d.oidc_configured ? 'Active' : 'Off';
|
||||
document.getElementById('ds-quotas-flag').textContent = d.quotas_enabled ? 'Enabled' : 'Disabled';
|
||||
document.getElementById('ds-auth').textContent = d.auth_enabled ? t('admin.enabled') : t('admin.disabled');
|
||||
document.getElementById('ds-oidc').textContent = d.oidc_configured ? t('admin.active') : t('admin.off');
|
||||
document.getElementById('ds-quotas-flag').textContent = d.quotas_enabled ? t('admin.enabled') : t('admin.disabled');
|
||||
|
||||
if (typeof d.registration_enabled !== 'undefined') {
|
||||
document.getElementById('ds-registration').checked = d.registration_enabled;
|
||||
@@ -101,14 +165,14 @@ async function loadDashboard() {
|
||||
|
||||
async function loadUsers() {
|
||||
const tbody = document.getElementById('users-tbody');
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="table-loading-cell"><i class="fas fa-spinner fa-spin"></i> Loading…</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="table-loading-cell"><i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.loading_users')) + '</td></tr>';
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users?limit=' + PAGE_SIZE + '&offset=' + (usersPage * PAGE_SIZE), { headers: headers(), credentials: 'same-origin' });
|
||||
if (!resp.ok) { tbody.innerHTML = '<tr><td colspan="7" class="table-status-error"><i class="fas fa-exclamation-circle"></i> Failed to load users</td></tr>'; return; }
|
||||
if (!resp.ok) { tbody.innerHTML = '<tr><td colspan="7" class="table-status-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('admin.failed_load_users')) + '</td></tr>'; return; }
|
||||
const data = await resp.json();
|
||||
totalUsers = data.total;
|
||||
const users = data.users;
|
||||
if (users.length === 0) { tbody.innerHTML = '<tr><td colspan="7" class="table-status-empty">No users found</td></tr>'; return; }
|
||||
if (users.length === 0) { tbody.innerHTML = '<tr><td colspan="7" class="table-status-empty">' + escapeHtml(t('admin.no_users_found')) + '</td></tr>'; return; }
|
||||
|
||||
tbody.innerHTML = users.map(u => {
|
||||
const quotaPct = u.storage_quota_bytes > 0 ? ((u.storage_used_bytes / u.storage_quota_bytes) * 100) : 0;
|
||||
@@ -118,20 +182,20 @@ async function loadUsers() {
|
||||
const isOidc = u.auth_provider && u.auth_provider !== 'local';
|
||||
const authBadge = isOidc
|
||||
? '<span class="badge badge-oidc" title="Authenticated via ' + escapeHtml(u.auth_provider) + '"><i class="fas fa-key badge-admin-icon-small"></i> ' + escapeHtml(u.auth_provider) + '</span>'
|
||||
: '<span class="badge badge-local">Local</span>';
|
||||
: '<span class="badge badge-local">' + escapeHtml(t('admin.local')) + '</span>';
|
||||
return '<tr>' +
|
||||
'<td><div class="user-info"><span class="user-name">' + escapeHtml(u.username) + (isSelf ? ' <span class="user-self-badge">(you)</span>' : '') + '</span><span class="user-email">' + escapeHtml(u.email) + '</span></div></td>' +
|
||||
'<td><div class="user-info"><span class="user-name">' + escapeHtml(u.username) + (isSelf ? ' <span class="user-self-badge">' + escapeHtml(t('admin.you_badge')) + '</span>' : '') + '</span><span class="user-email">' + escapeHtml(u.email) + '</span></div></td>' +
|
||||
'<td><span class="badge badge-' + escapeHtml(u.role) + '">' + (u.role === 'admin' ? '<i class="fas fa-shield-alt badge-admin-icon-small"></i> ' : '') + escapeHtml(u.role) + '</span></td>' +
|
||||
'<td>' + authBadge + '</td>' +
|
||||
'<td><span class="badge badge-' + (u.active ? 'active' : 'inactive') + '">' + (u.active ? 'Active' : 'Inactive') + '</span></td>' +
|
||||
'<td><span class="badge badge-' + (u.active ? 'active' : 'inactive') + '">' + (u.active ? escapeHtml(t('admin.active')) : escapeHtml(t('admin.inactive'))) + '</span></td>' +
|
||||
'<td><div class="quota-bar"><div class="progress-bar quota-progress-fixed"><div class="progress-fill ' + quotaColor + '" data-width="' + Math.min(quotaPct, 100) + '"></div></div><span class="quota-text">' + quotaText + '</span></div></td>' +
|
||||
'<td class="user-last-login-cell">' + timeAgo(u.last_login_at) + '</td>' +
|
||||
'<td><div class="actions-row">' +
|
||||
'<button class="btn btn-sm btn-secondary admin-action-btn" data-action="quota" data-uid="' + _escJs(u.id) + '" data-uname="' + _escJs(u.username) + '" data-quota="' + u.storage_quota_bytes + '" title="Edit quota"><i class="fas fa-box"></i></button>' +
|
||||
(isOidc ? '' : '<button class="btn btn-sm btn-secondary admin-action-btn" data-action="reset-pw" data-uid="' + _escJs(u.id) + '" data-uname="' + _escJs(u.username) + '" title="Reset password"><i class="fas fa-key"></i></button>') +
|
||||
'<button class="btn btn-sm btn-secondary admin-action-btn" data-action="toggle-role" data-uid="' + _escJs(u.id) + '" data-role="' + _escJs(u.role) + '" title="Toggle role"' + (isSelf ? ' disabled' : '') + '><i class="fas fa-' + (u.role === 'admin' ? 'user' : 'crown') + '"></i></button>' +
|
||||
'<button class="btn btn-sm ' + (u.active ? 'btn-danger' : 'btn-success') + ' admin-action-btn" data-action="toggle-active" data-uid="' + _escJs(u.id) + '" data-active="' + u.active + '" title="' + (u.active ? 'Deactivate' : 'Activate') + '"' + (isSelf && u.active ? ' disabled' : '') + '><i class="fas fa-' + (u.active ? 'ban' : 'check') + '"></i></button>' +
|
||||
'<button class="btn btn-sm btn-danger admin-action-btn" data-action="delete" data-uid="' + _escJs(u.id) + '" data-uname="' + _escJs(u.username) + '" title="Delete"' + (isSelf ? ' disabled' : '') + '><i class="fas fa-trash-alt"></i></button>' +
|
||||
'<button class="btn btn-sm btn-secondary admin-action-btn" data-action="quota" data-uid="' + _escJs(u.id) + '" data-uname="' + _escJs(u.username) + '" data-quota="' + u.storage_quota_bytes + '" title="' + escapeHtml(t('admin.edit_quota_title')) + '"><i class="fas fa-box"></i></button>' +
|
||||
(isOidc ? '' : '<button class="btn btn-sm btn-secondary admin-action-btn" data-action="reset-pw" data-uid="' + _escJs(u.id) + '" data-uname="' + _escJs(u.username) + '" title="' + escapeHtml(t('admin.reset_password_title')) + '"><i class="fas fa-key"></i></button>') +
|
||||
'<button class="btn btn-sm btn-secondary admin-action-btn" data-action="toggle-role" data-uid="' + _escJs(u.id) + '" data-role="' + _escJs(u.role) + '" title="' + escapeHtml(t('admin.toggle_role_title')) + '"' + (isSelf ? ' disabled' : '') + '><i class="fas fa-' + (u.role === 'admin' ? 'user' : 'crown') + '"></i></button>' +
|
||||
'<button class="btn btn-sm ' + (u.active ? 'btn-danger' : 'btn-success') + ' admin-action-btn" data-action="toggle-active" data-uid="' + _escJs(u.id) + '" data-active="' + u.active + '" title="' + (u.active ? escapeHtml(t('admin.deactivate_title')) : escapeHtml(t('admin.activate_title'))) + '"' + (isSelf && u.active ? ' disabled' : '') + '><i class="fas fa-' + (u.active ? 'ban' : 'check') + '"></i></button>' +
|
||||
'<button class="btn btn-sm btn-danger admin-action-btn" data-action="delete" data-uid="' + _escJs(u.id) + '" data-uname="' + _escJs(u.username) + '" title="' + escapeHtml(t('admin.delete_title')) + '"' + (isSelf ? ' disabled' : '') + '><i class="fas fa-trash-alt"></i></button>' +
|
||||
'</div></td></tr>';
|
||||
}).join('');
|
||||
|
||||
@@ -153,11 +217,13 @@ async function loadUsers() {
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('users-info').textContent = 'Showing ' + (usersPage * PAGE_SIZE + 1) + '-' + Math.min((usersPage + 1) * PAGE_SIZE, totalUsers) + ' of ' + totalUsers;
|
||||
const from = usersPage * PAGE_SIZE + 1;
|
||||
const to = Math.min((usersPage + 1) * PAGE_SIZE, totalUsers);
|
||||
document.getElementById('users-info').textContent = t('admin.showing_users', { from: from, to: to, total: totalUsers });
|
||||
document.getElementById('prev-btn').disabled = usersPage === 0;
|
||||
document.getElementById('next-btn').disabled = (usersPage + 1) * PAGE_SIZE >= totalUsers;
|
||||
} catch (e) {
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="table-status-error"><i class="fas fa-exclamation-circle"></i> Error: ' + escapeHtml(e.message) + '</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="table-status-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('admin.error_network', { message: e.message })) + '</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,32 +232,35 @@ function nextPage() { if ((usersPage + 1) * PAGE_SIZE < totalUsers) { usersPage+
|
||||
|
||||
async function toggleRole(userId, currentRole) {
|
||||
const newRole = currentRole === 'admin' ? 'user' : 'admin';
|
||||
if (!confirm('Change role to ' + newRole + '?')) return;
|
||||
const ok = await showConfirm(t('admin.confirm_role_change', { role: newRole }));
|
||||
if (!ok) return;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + userId + '/role', {
|
||||
method: 'PUT', headers: headers(), credentials: 'same-origin', body: JSON.stringify({ role: newRole })
|
||||
});
|
||||
if (resp.ok) loadUsers(); else { const e = await resp.json(); alert(e.message || 'Failed'); }
|
||||
} catch (e) { alert('Error: ' + e.message); }
|
||||
if (resp.ok) loadUsers(); else { const e = await resp.json(); alert(e.message || t('admin.error_generic')); }
|
||||
} catch (e) { alert(t('admin.error_network', { message: e.message })); }
|
||||
}
|
||||
|
||||
async function toggleActive(userId, currentActive) {
|
||||
const action = currentActive ? 'deactivate' : 'activate';
|
||||
if (!confirm('Are you sure you want to ' + action + ' this user?')) return;
|
||||
const msg = currentActive ? t('admin.confirm_deactivate') : t('admin.confirm_activate');
|
||||
const ok = await showConfirm(msg);
|
||||
if (!ok) return;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + userId + '/active', {
|
||||
method: 'PUT', headers: headers(), credentials: 'same-origin', body: JSON.stringify({ active: !currentActive })
|
||||
});
|
||||
if (resp.ok) loadUsers(); else { const e = await resp.json(); alert(e.message || 'Failed'); }
|
||||
} catch (e) { alert('Error: ' + e.message); }
|
||||
if (resp.ok) loadUsers(); else { const e = await resp.json(); alert(e.message || t('admin.error_generic')); }
|
||||
} catch (e) { alert(t('admin.error_network', { message: e.message })); }
|
||||
}
|
||||
|
||||
async function deleteUser(userId, username) {
|
||||
if (!confirm('DELETE user "' + username + '"? This cannot be undone!')) return;
|
||||
const ok = await showConfirm(t('admin.confirm_delete_user', { name: username }));
|
||||
if (!ok) return;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + userId, { method: 'DELETE', headers: headers(), credentials: 'same-origin' });
|
||||
if (resp.ok) { loadUsers(); loadDashboard(); } else { const e = await resp.json(); alert(e.message || 'Failed'); }
|
||||
} catch (e) { alert('Error: ' + e.message); }
|
||||
if (resp.ok) { loadUsers(); loadDashboard(); } else { const e = await resp.json(); alert(e.message || t('admin.error_generic')); }
|
||||
} catch (e) { alert(t('admin.error_network', { message: e.message })); }
|
||||
}
|
||||
|
||||
let quotaUserId = '';
|
||||
@@ -214,8 +283,8 @@ async function saveQuota() {
|
||||
method: 'PUT', headers: headers(), credentials: 'same-origin', body: JSON.stringify({ quota_bytes: bytes })
|
||||
});
|
||||
if (resp.ok) { closeQuotaModal(); loadUsers(); loadDashboard(); }
|
||||
else { const e = await resp.json(); alert(e.message || 'Failed'); }
|
||||
} catch (e) { alert('Error: ' + e.message); }
|
||||
else { const e = await resp.json(); alert(e.message || t('admin.error_generic')); }
|
||||
} catch (e) { alert(t('admin.error_network', { message: e.message })); }
|
||||
}
|
||||
|
||||
function openCreateUserModal() {
|
||||
@@ -242,11 +311,11 @@ async function submitCreateUser() {
|
||||
const quotaBytes = Math.round(quotaVal * quotaUnit);
|
||||
|
||||
const errorEl = document.getElementById('cu-error');
|
||||
if (username.length < 3) { errorEl.textContent = 'Username must be at least 3 characters'; errorEl.className = 'alert alert-error'; return; }
|
||||
if (password.length < 8) { errorEl.textContent = 'Password must be at least 8 characters'; errorEl.className = 'alert alert-error'; return; }
|
||||
if (username.length < 3) { errorEl.textContent = t('admin.error_username_short'); errorEl.className = 'alert alert-error'; return; }
|
||||
if (password.length < 8) { errorEl.textContent = t('admin.error_password_short'); errorEl.className = 'alert alert-error'; return; }
|
||||
|
||||
const btn = document.getElementById('cu-submit');
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Creating…';
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.creating'));
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users', {
|
||||
method: 'POST', headers: headers(), credentials: 'same-origin',
|
||||
@@ -258,14 +327,14 @@ async function submitCreateUser() {
|
||||
loadDashboard();
|
||||
} else {
|
||||
const e = await resp.json().catch(() => ({}));
|
||||
errorEl.textContent = e.message || 'Failed to create user';
|
||||
errorEl.textContent = e.message || t('admin.error_create_user');
|
||||
errorEl.className = 'alert alert-error';
|
||||
}
|
||||
} catch (e) {
|
||||
errorEl.textContent = 'Network error: ' + e.message;
|
||||
errorEl.textContent = t('admin.error_network', { message: e.message });
|
||||
errorEl.className = 'alert alert-error';
|
||||
}
|
||||
btn.disabled = false; btn.innerHTML = '<i class="fas fa-user-plus"></i> Create';
|
||||
btn.disabled = false; btn.innerHTML = '<i class="fas fa-user-plus"></i> ' + escapeHtml(t('admin.create_user'));
|
||||
}
|
||||
|
||||
let resetPwUserId = '';
|
||||
@@ -283,19 +352,19 @@ function closeResetPasswordModal() { hideElement('reset-pw-modal'); }
|
||||
async function submitResetPassword() {
|
||||
const password = document.getElementById('rp-password').value;
|
||||
const errorEl = document.getElementById('rp-error');
|
||||
if (password.length < 8) { errorEl.textContent = 'Password must be at least 8 characters'; errorEl.className = 'alert alert-error'; return; }
|
||||
if (password.length < 8) { errorEl.textContent = t('admin.error_password_short'); errorEl.className = 'alert alert-error'; return; }
|
||||
|
||||
const btn = document.getElementById('rp-submit');
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Resetting…';
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.resetting'));
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + resetPwUserId + '/password', {
|
||||
method: 'PUT', headers: headers(), credentials: 'same-origin',
|
||||
body: JSON.stringify({ new_password: password })
|
||||
});
|
||||
if (resp.ok) { closeResetPasswordModal(); }
|
||||
else { const e = await resp.json().catch(() => ({})); errorEl.textContent = e.message || 'Failed'; errorEl.className = 'alert alert-error'; }
|
||||
} catch (e) { errorEl.textContent = 'Error: ' + e.message; errorEl.className = 'alert alert-error'; }
|
||||
btn.disabled = false; btn.innerHTML = '<i class="fas fa-save"></i> Reset';
|
||||
else { const e = await resp.json().catch(() => ({})); errorEl.textContent = e.message || t('admin.error_generic'); errorEl.className = 'alert alert-error'; }
|
||||
} catch (e) { errorEl.textContent = t('admin.error_network', { message: e.message }); errorEl.className = 'alert alert-error'; }
|
||||
btn.disabled = false; btn.innerHTML = '<i class="fas fa-save"></i> ' + escapeHtml(t('admin.reset_btn'));
|
||||
}
|
||||
|
||||
async function toggleRegistration(enabled) {
|
||||
@@ -311,13 +380,13 @@ async function toggleRegistration(enabled) {
|
||||
if (!enabled) showElement('registration-warning', 'flex');
|
||||
else hideElement('registration-warning');
|
||||
const e = await resp.json().catch(() => ({}));
|
||||
alert(e.message || 'Failed to update registration setting');
|
||||
alert(e.message || t('admin.error_generic'));
|
||||
}
|
||||
} catch (e) {
|
||||
document.getElementById('ds-registration').checked = !enabled;
|
||||
if (!enabled) showElement('registration-warning', 'flex');
|
||||
else hideElement('registration-warning');
|
||||
alert('Error: ' + e.message);
|
||||
alert(t('admin.error_network', { message: e.message }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,7 +414,7 @@ async function testConnection() {
|
||||
const url = document.getElementById('issuer-url').value.trim();
|
||||
if (!url) { showOidcStatus('Enter an Issuer URL first', 'error'); return; }
|
||||
const btn = document.getElementById('discover-btn');
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Discovering…';
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.discovering'));
|
||||
const resultDiv = document.getElementById('discovery-result');
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/settings/oidc/test', { method: 'POST', headers: headers(), credentials: 'same-origin', body: JSON.stringify({ issuer_url: url }) });
|
||||
@@ -357,12 +426,12 @@ async function testConnection() {
|
||||
resultDiv.innerHTML = '<div class="discovery-result fail"><strong><i class="fas fa-times-circle"></i> ' + escapeHtml(r.message) + '</strong></div>';
|
||||
}
|
||||
} catch (e) { resultDiv.innerHTML = '<div class="discovery-result fail"><i class="fas fa-times-circle"></i> Error: ' + escapeHtml(e.message) + '</div>'; }
|
||||
btn.disabled = false; btn.innerHTML = '<i class="fas fa-search"></i> Auto-discover';
|
||||
btn.disabled = false; btn.innerHTML = '<i class="fas fa-search"></i> ' + escapeHtml(t('admin.auto_discover'));
|
||||
}
|
||||
|
||||
async function saveOidcSettings() {
|
||||
const btn = document.getElementById('save-btn');
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving…';
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.saving'));
|
||||
const body = {
|
||||
enabled: document.getElementById('oidc-enabled').checked,
|
||||
issuer_url: document.getElementById('issuer-url').value.trim(),
|
||||
@@ -376,10 +445,14 @@ async function saveOidcSettings() {
|
||||
};
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/settings/oidc', { method: 'PUT', headers: headers(), credentials: 'same-origin', body: JSON.stringify(body) });
|
||||
if (resp.ok) { showOidcStatus('Settings saved — OIDC is now ' + (body.enabled ? 'active' : 'disabled'), 'success'); loadDashboard(); }
|
||||
if (resp.ok) {
|
||||
const status = body.enabled ? t('admin.active').toLowerCase() : t('admin.disabled').toLowerCase();
|
||||
showOidcStatus(t('admin.settings_saved', { status: status }), 'success');
|
||||
loadDashboard();
|
||||
}
|
||||
else { const e = await resp.json().catch(()=>({})); showOidcStatus('Error: ' + (e.message || resp.statusText), 'error'); }
|
||||
} catch (e) { showOidcStatus('Network error: ' + e.message, 'error'); }
|
||||
btn.disabled = false; btn.innerHTML = '<i class="fas fa-save"></i> Save';
|
||||
} catch (e) { showOidcStatus(t('admin.error_network', { message: e.message }), 'error'); }
|
||||
btn.disabled = false; btn.innerHTML = '<i class="fas fa-save"></i> ' + escapeHtml(t('admin.save_btn'));
|
||||
}
|
||||
|
||||
async function init() {
|
||||
@@ -424,6 +497,19 @@ function showAccessDenied() {
|
||||
showElement('access-denied');
|
||||
}
|
||||
|
||||
/* ── Apply i18n when translations load / change ── */
|
||||
document.addEventListener('translationsLoaded', function() {
|
||||
if (window.i18n && window.i18n.translatePage) window.i18n.translatePage();
|
||||
// Re-render dynamic content that uses t()
|
||||
loadDashboard();
|
||||
if (activeTabName === 'users') loadUsers();
|
||||
});
|
||||
document.addEventListener('localeChanged', function() {
|
||||
if (window.i18n && window.i18n.translatePage) window.i18n.translatePage();
|
||||
loadDashboard();
|
||||
if (activeTabName === 'users') loadUsers();
|
||||
});
|
||||
|
||||
init();
|
||||
|
||||
/* ── Event-listener wiring (replaces inline onclick/onchange) ── */
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
const API = '/api';
|
||||
|
||||
/* ── i18n helper — falls back to key if i18n not ready ── */
|
||||
function t(key, params) {
|
||||
if (window.i18n && typeof window.i18n.t === 'function') return window.i18n.t(key, params);
|
||||
return key.split('.').pop().replace(/_/g, ' ');
|
||||
}
|
||||
|
||||
function headers() {
|
||||
return { 'Content-Type': 'application/json', ...getCsrfHeaders() };
|
||||
}
|
||||
@@ -12,14 +18,14 @@ function formatBytes(bytes) {
|
||||
}
|
||||
|
||||
function timeAgo(dateStr) {
|
||||
if (!dateStr) return 'Never';
|
||||
if (!dateStr) return t('profile.never');
|
||||
const d = new Date(dateStr);
|
||||
const now = new Date();
|
||||
const secs = Math.floor((now - d) / 1000);
|
||||
if (secs < 60) return 'Just now';
|
||||
if (secs < 3600) return Math.floor(secs/60) + ' min ago';
|
||||
if (secs < 86400) return Math.floor(secs/3600) + 'h ago';
|
||||
if (secs < 2592000) return Math.floor(secs/86400) + ' days ago';
|
||||
if (secs < 60) return t('profile.just_now');
|
||||
if (secs < 3600) return t('profile.minutes_ago', { n: Math.floor(secs/60) });
|
||||
if (secs < 86400) return t('profile.hours_ago', { n: Math.floor(secs/3600) });
|
||||
if (secs < 2592000) return t('profile.days_ago', { n: Math.floor(secs/86400) });
|
||||
return d.toLocaleDateString();
|
||||
}
|
||||
|
||||
@@ -37,15 +43,15 @@ async function init() {
|
||||
const badge = document.getElementById('p-role-badge');
|
||||
if (user.role === 'admin') {
|
||||
badge.className = 'role-badge role-badge-admin';
|
||||
badge.innerHTML = '<i class="fas fa-shield-alt"></i> Administrator';
|
||||
badge.innerHTML = '<i class="fas fa-shield-alt"></i> ' + t('profile.role_admin');
|
||||
} else {
|
||||
badge.className = 'role-badge role-badge-user';
|
||||
badge.innerHTML = '<i class="fas fa-user"></i> User';
|
||||
badge.innerHTML = '<i class="fas fa-user"></i> ' + t('profile.role_user');
|
||||
}
|
||||
|
||||
document.getElementById('p-detail-username').textContent = user.username;
|
||||
document.getElementById('p-detail-email').textContent = user.email || '—';
|
||||
document.getElementById('p-detail-role').textContent = user.role === 'admin' ? 'Administrator' : 'User';
|
||||
document.getElementById('p-detail-role').textContent = user.role === 'admin' ? t('profile.role_admin') : t('profile.role_user');
|
||||
document.getElementById('p-detail-login').textContent = timeAgo(user.last_login_at);
|
||||
|
||||
const used = user.storage_used_bytes || 0;
|
||||
@@ -59,7 +65,7 @@ async function init() {
|
||||
const bar = document.getElementById('p-storage-bar');
|
||||
bar.style.width = pct + '%';
|
||||
bar.className = 'storage-fill ' + (pct > 90 ? 'red' : pct > 70 ? 'orange' : 'green');
|
||||
document.getElementById('p-storage-text').textContent = formatBytes(used) + ' / ' + (quota > 0 ? formatBytes(quota) : 'Unlimited');
|
||||
document.getElementById('p-storage-text').textContent = formatBytes(used) + ' / ' + (quota > 0 ? formatBytes(quota) : t('profile.unlimited'));
|
||||
|
||||
if (user.auth_provider && user.auth_provider !== 'local') {
|
||||
document.getElementById('password-section').classList.add('hidden');
|
||||
@@ -99,18 +105,18 @@ async function changePassword(e) {
|
||||
const statusEl = document.getElementById('pw-status');
|
||||
|
||||
if (newPw !== confirmPw) {
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> Passwords do not match</div>';
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('profile.passwords_no_match')) + '</div>';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newPw.length < 8) {
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> Password must be at least 8 characters</div>';
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('profile.password_too_short')) + '</div>';
|
||||
return false;
|
||||
}
|
||||
|
||||
const btn = document.getElementById('pw-submit');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Updating…';
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('profile.updating'));
|
||||
|
||||
try {
|
||||
const resp = await fetch(API + '/auth/change-password', {
|
||||
@@ -121,18 +127,18 @@ async function changePassword(e) {
|
||||
});
|
||||
|
||||
if (resp.ok) {
|
||||
statusEl.innerHTML = '<div class="alert alert-success"><i class="fas fa-check-circle"></i> Password updated successfully</div>';
|
||||
statusEl.innerHTML = '<div class="alert alert-success"><i class="fas fa-check-circle"></i> ' + escapeHtml(t('profile.password_updated')) + '</div>';
|
||||
document.getElementById('password-form').reset();
|
||||
} else {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(err.message || 'Failed to change password') + '</div>';
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(err.message || t('profile.password_change_failed')) + '</div>';
|
||||
}
|
||||
} catch (err) {
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> Network error: ' + escapeHtml(err.message) + '</div>';
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('profile.error_network', { message: err.message })) + '</div>';
|
||||
}
|
||||
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-save"></i> Update Password';
|
||||
btn.innerHTML = '<i class="fas fa-save"></i> ' + escapeHtml(t('profile.update_password'));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -151,15 +157,15 @@ function renderPwRow(pw) {
|
||||
const created = document.createElement('td');
|
||||
created.textContent = new Date(pw.created_at).toLocaleDateString();
|
||||
const lastUsed = document.createElement('td');
|
||||
lastUsed.textContent = pw.last_used_at ? timeAgo(pw.last_used_at) : 'Never';
|
||||
lastUsed.textContent = pw.last_used_at ? timeAgo(pw.last_used_at) : t('profile.never');
|
||||
const status = document.createElement('td');
|
||||
const badge = document.createElement('span');
|
||||
if (pw.active !== false) {
|
||||
badge.className = 'badge badge-active';
|
||||
badge.textContent = 'Active';
|
||||
badge.textContent = t('profile.active');
|
||||
} else {
|
||||
badge.className = 'badge badge-expired';
|
||||
badge.textContent = 'Revoked';
|
||||
badge.textContent = t('profile.revoked');
|
||||
}
|
||||
status.appendChild(badge);
|
||||
const actions = document.createElement('td');
|
||||
@@ -167,7 +173,7 @@ function renderPwRow(pw) {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'btn btn-danger-sm';
|
||||
btn.innerHTML = '<i class="fas fa-trash"></i>';
|
||||
btn.title = 'Revoke';
|
||||
btn.title = t('profile.revoke_title');
|
||||
btn.addEventListener('click', function () { revokeAppPassword(pw.id, pw.label); });
|
||||
actions.appendChild(btn);
|
||||
}
|
||||
@@ -232,12 +238,12 @@ async function createAppPassword() {
|
||||
const btn = document.getElementById('app-pw-generate');
|
||||
|
||||
if (!label) {
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> Please enter a label</div>';
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('profile.error_label_required')) + '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Generating…';
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('profile.generating'));
|
||||
statusEl.innerHTML = '';
|
||||
|
||||
try {
|
||||
@@ -249,7 +255,7 @@ async function createAppPassword() {
|
||||
});
|
||||
if (!resp.ok) {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + (err.message || 'Failed to create app password') + '</div>';
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(err.message || t('profile.error_create_pw')) + '</div>';
|
||||
return;
|
||||
}
|
||||
const result = await resp.json();
|
||||
@@ -262,7 +268,7 @@ async function createAppPassword() {
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + err.message + '</div>';
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-plus"></i> Generate';
|
||||
btn.innerHTML = '<i class="fas fa-plus"></i> ' + escapeHtml(t('profile.generate'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +282,7 @@ function copyAppPassword() {
|
||||
}
|
||||
|
||||
async function revokeAppPassword(id, label) {
|
||||
if (!confirm('Revoke app password "' + label + '"? Clients using this password will stop working.')) return;
|
||||
if (!confirm(t('profile.confirm_revoke', { label: label }))) return;
|
||||
try {
|
||||
const resp = await fetch(API + '/auth/app-passwords/' + encodeURIComponent(id), {
|
||||
method: 'DELETE',
|
||||
@@ -288,10 +294,10 @@ async function revokeAppPassword(id, label) {
|
||||
loadAppPasswords();
|
||||
} else {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
alert(err.message || 'Failed to revoke app password');
|
||||
alert(err.message || t('profile.error_revoke'));
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Network error: ' + err.message);
|
||||
alert(t('profile.error_network', { message: err.message }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,3 +314,7 @@ document.getElementById('password-form').addEventListener('submit', changePasswo
|
||||
document.getElementById('app-pw-generate').addEventListener('click', createAppPassword);
|
||||
document.getElementById('app-pw-copy-btn').addEventListener('click', copyAppPassword);
|
||||
document.getElementById('app-pw-auto-toggle').addEventListener('click', toggleAutoPasswords);
|
||||
|
||||
/* Re-render when language changes */
|
||||
window.addEventListener('translationsLoaded', function () { init(); });
|
||||
window.addEventListener('localeChanged', function () { init(); });
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
{
|
||||
"app": {
|
||||
"title": "OxiCloud",
|
||||
"description": "نظام تخزين سحابي بسيط"
|
||||
},
|
||||
"nav": {
|
||||
"files": "الملفات",
|
||||
"shared": "المشترك",
|
||||
"recent": "الأخيرة",
|
||||
"favorites": "المفضلة",
|
||||
"photos": "الصور",
|
||||
"trash": "سلة المهملات"
|
||||
},
|
||||
"photos": {
|
||||
"empty_state": "لا توجد صور بعد",
|
||||
"empty_hint": "ارفع صوراً أو مقاطع فيديو لعرضها هنا",
|
||||
"items_selected": "محدد",
|
||||
"view_daily": "يوم",
|
||||
"view_monthly": "شهر",
|
||||
"view_yearly": "سنة"
|
||||
},
|
||||
"actions": {
|
||||
"search": "البحث في الملفات...",
|
||||
"new_folder": "مجلد جديد",
|
||||
"upload": "رفع",
|
||||
"upload_files": "رفع ملفات",
|
||||
"upload_folder": "رفع مجلد",
|
||||
"upload.uploading": "جارٍ الرفع...",
|
||||
"upload.complete": "{count} / {total} تم رفعها",
|
||||
"rename": "إعادة التسمية",
|
||||
"move": "نقل إلى...",
|
||||
"move_to": "نقل إلى",
|
||||
"delete": "حذف",
|
||||
"download": "تحميل",
|
||||
"view": "عرض",
|
||||
"cancel": "إلغاء",
|
||||
"confirm": "تأكيد",
|
||||
"share": "مشاركة",
|
||||
"favorite": "إضافة للمفضلة",
|
||||
"unfavorite": "إزالة من المفضلة",
|
||||
"copy": "نسخ",
|
||||
"notify": "إشعار",
|
||||
"send": "إرسال",
|
||||
"clear_recent": "مسح الأخيرة",
|
||||
"logout": "تسجيل الخروج",
|
||||
"create": "إنشاء",
|
||||
"search_btn": "بحث",
|
||||
"close": "إغلاق",
|
||||
"delete_permanently": "حذف نهائياً",
|
||||
"empty_trash": "تفريغ سلة المهملات"
|
||||
},
|
||||
"user_menu": {
|
||||
"appearance": "المظهر",
|
||||
"about": "حول OxiCloud",
|
||||
"about_description": "منصة تخزين سحابي مبنية بـ Rust و Clean Architecture. سريعة وآمنة وخاصة.",
|
||||
"admin_panel": "لوحة الإدارة",
|
||||
"profile": "ملفي الشخصي",
|
||||
"role_user": "مستخدم"
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "رابط المشاركة",
|
||||
"linkLabel": "رابط المشاركة:",
|
||||
"copyLink": "نسخ",
|
||||
"permissions": "الصلاحيات:",
|
||||
"permissionRead": "قراءة",
|
||||
"permissionWrite": "كتابة",
|
||||
"permissionReshare": "إعادة مشاركة",
|
||||
"password": "حماية بكلمة مرور:",
|
||||
"generatePassword": "توليد",
|
||||
"expiration": "تاريخ انتهاء الصلاحية:",
|
||||
"update": "تحديث المشاركة",
|
||||
"remove": "إزالة المشاركة",
|
||||
"notifyTitle": "إرسال إشعار",
|
||||
"notifyEmailLabel": "عنوان البريد الإلكتروني:",
|
||||
"notifyMessageLabel": "رسالة (اختياري):",
|
||||
"notifySend": "إرسال الإشعار",
|
||||
"shareWithOthers": "مشاركة مع آخرين",
|
||||
"sharePublicly": "مشاركة عامة",
|
||||
"shareSettings": "إعدادات المشاركة",
|
||||
"shareCopied": "تم نسخ الرابط إلى الحافظة",
|
||||
"shareCreated": "تم إنشاء رابط المشاركة بنجاح",
|
||||
"shareUpdated": "تم تحديث إعدادات المشاركة بنجاح",
|
||||
"shareRemoved": "تمت إزالة المشاركة بنجاح"
|
||||
},
|
||||
"share_dialogTitle": "رابط المشاركة",
|
||||
"share_linkLabel": "رابط المشاركة:",
|
||||
"share_copyLink": "نسخ",
|
||||
"share_permissions": "الصلاحيات:",
|
||||
"share_permissionRead": "قراءة",
|
||||
"share_permissionWrite": "كتابة",
|
||||
"share_permissionReshare": "إعادة مشاركة",
|
||||
"share_password": "حماية بكلمة مرور:",
|
||||
"share_generatePassword": "توليد",
|
||||
"share_expiration": "تاريخ انتهاء الصلاحية:",
|
||||
"share_update": "تحديث المشاركة",
|
||||
"share_remove": "إزالة المشاركة",
|
||||
"share_notifyTitle": "إرسال إشعار",
|
||||
"share_notifyEmailLabel": "عنوان البريد الإلكتروني:",
|
||||
"share_notifyMessageLabel": "رسالة (اختياري):",
|
||||
"share_notifySend": "إرسال الإشعار",
|
||||
"shared": {
|
||||
"backToFiles": "العودة إلى الملفات",
|
||||
"pageTitle": "الموارد المشتركة",
|
||||
"pageDescription": "إدارة ملفاتك ومجلداتك المشتركة",
|
||||
"filterType": "النوع:",
|
||||
"filterAll": "الكل",
|
||||
"filterFiles": "ملفات",
|
||||
"filterFolders": "مجلدات",
|
||||
"sortBy": "ترتيب حسب:",
|
||||
"sortByName": "الاسم",
|
||||
"sortByDate": "تاريخ المشاركة",
|
||||
"sortByExpiration": "انتهاء الصلاحية",
|
||||
"search": "بحث",
|
||||
"colName": "الاسم",
|
||||
"colType": "النوع",
|
||||
"colDateShared": "تاريخ المشاركة",
|
||||
"colExpiration": "انتهاء الصلاحية",
|
||||
"colPermissions": "الصلاحيات",
|
||||
"colPassword": "كلمة المرور",
|
||||
"colActions": "الإجراءات",
|
||||
"emptyStateTitle": "لا توجد موارد مشتركة بعد",
|
||||
"emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا",
|
||||
"goToFiles": "الذهاب إلى الملفات",
|
||||
"typeFile": "ملف",
|
||||
"typeFolder": "مجلد",
|
||||
"noExpiration": "بدون انتهاء صلاحية",
|
||||
"hasPassword": "نعم",
|
||||
"noPassword": "لا",
|
||||
"editShare": "تعديل المشاركة",
|
||||
"notifyShare": "إشعار شخص ما",
|
||||
"copyLink": "نسخ الرابط",
|
||||
"removeShare": "إزالة المشاركة",
|
||||
"linkCopied": "تم نسخ الرابط إلى الحافظة!",
|
||||
"linkCopyFailed": "فشل نسخ الرابط",
|
||||
"itemUpdated": "تم تحديث إعدادات المشاركة بنجاح",
|
||||
"itemRemoved": "تمت إزالة المشاركة بنجاح",
|
||||
"invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح",
|
||||
"notificationSent": "تم إرسال الإشعار بنجاح",
|
||||
"notificationFailed": "فشل إرسال الإشعار",
|
||||
"shared_backToFiles": "العودة إلى الملفات",
|
||||
"shared_pageTitle": "الموارد المشتركة",
|
||||
"shared_pageDescription": "إدارة ملفاتك ومجلداتك المشتركة",
|
||||
"shared_filterType": "النوع:",
|
||||
"shared_filterAll": "الكل",
|
||||
"shared_filterFiles": "ملفات",
|
||||
"shared_filterFolders": "مجلدات",
|
||||
"shared_sortBy": "ترتيب حسب:",
|
||||
"shared_sortByName": "الاسم",
|
||||
"shared_sortByDate": "تاريخ المشاركة",
|
||||
"shared_sortByExpiration": "انتهاء الصلاحية",
|
||||
"shared_search": "بحث",
|
||||
"shared_colName": "الاسم",
|
||||
"shared_colType": "النوع",
|
||||
"shared_colDateShared": "تاريخ المشاركة",
|
||||
"shared_colExpiration": "انتهاء الصلاحية",
|
||||
"shared_colPermissions": "الصلاحيات",
|
||||
"shared_colPassword": "كلمة المرور",
|
||||
"shared_colActions": "الإجراءات",
|
||||
"shared_emptyStateTitle": "لا توجد موارد مشتركة بعد",
|
||||
"shared_emptyStateDesc": "عندما تشارك ملفات أو مجلدات، ستظهر هنا",
|
||||
"shared_goToFiles": "الذهاب إلى الملفات",
|
||||
"shared_typeFile": "ملف",
|
||||
"shared_typeFolder": "مجلد",
|
||||
"shared_noExpiration": "بدون انتهاء صلاحية",
|
||||
"shared_hasPassword": "نعم",
|
||||
"shared_noPassword": "لا",
|
||||
"shared_editShare": "تعديل المشاركة",
|
||||
"shared_notifyShare": "إشعار شخص ما",
|
||||
"shared_copyLink": "نسخ الرابط",
|
||||
"shared_removeShare": "إزالة المشاركة",
|
||||
"shared_linkCopied": "تم نسخ الرابط إلى الحافظة!",
|
||||
"shared_linkCopyFailed": "فشل نسخ الرابط",
|
||||
"shared_itemUpdated": "تم تحديث إعدادات المشاركة بنجاح",
|
||||
"shared_itemRemoved": "تمت إزالة المشاركة بنجاح",
|
||||
"shared_invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح",
|
||||
"shared_notificationSent": "تم إرسال الإشعار بنجاح",
|
||||
"shared_notificationFailed": "فشل إرسال الإشعار"
|
||||
},
|
||||
"files": {
|
||||
"name": "الاسم",
|
||||
"type": "النوع",
|
||||
"size": "الحجم",
|
||||
"modified": "تاريخ التعديل",
|
||||
"no_files": "لا توجد ملفات في هذا المجلد",
|
||||
"empty_hint": "ارفع ملفات أو أنشئ مجلدات للبدء",
|
||||
"loading": "جارٍ تحميل الملفات…",
|
||||
"view_grid": "عرض شبكي",
|
||||
"view_list": "عرض قائمة",
|
||||
"file_types": {
|
||||
"document": "مستند",
|
||||
"image": "صورة",
|
||||
"video": "فيديو",
|
||||
"audio": "صوت",
|
||||
"pdf": "PDF",
|
||||
"text": "نص",
|
||||
"folder": "مجلد",
|
||||
"spreadsheet": "جدول بيانات",
|
||||
"presentation": "عرض تقديمي",
|
||||
"archive": "أرشيف",
|
||||
"installer": "مثبّت",
|
||||
"code": "كود"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
"rename_folder": "إعادة تسمية المجلد",
|
||||
"rename_file": "إعادة تسمية الملف",
|
||||
"new_name": "الاسم الجديد",
|
||||
"new_folder_title": "مجلد جديد",
|
||||
"folder_name": "اسم المجلد",
|
||||
"folder_placeholder": "مجلدي",
|
||||
"rename_title": "إعادة التسمية",
|
||||
"move_file": "نقل الملف",
|
||||
"move_folder": "نقل المجلد",
|
||||
"select_destination": "اختر المجلد الوجهة:",
|
||||
"select_this_folder": "اختيار هذا المجلد",
|
||||
"go_to_parent": ".. (المجلد الأعلى)",
|
||||
"no_subfolders": "لا توجد مجلدات فرعية",
|
||||
"root": "الجذر",
|
||||
"delete_confirmation": "هل أنت متأكد أنك تريد حذف",
|
||||
"and_contents": "وجميع محتوياته",
|
||||
"no_undo": "لا يمكن التراجع عن هذا الإجراء",
|
||||
"confirm_title": "تأكيد الإجراء",
|
||||
"confirm_delete": "نقل إلى سلة المهملات",
|
||||
"confirm_delete_file": "هل أنت متأكد أنك تريد نقل الملف \"{{name}}\" إلى سلة المهملات؟",
|
||||
"confirm_delete_folder": "هل أنت متأكد أنك تريد نقل المجلد \"{{name}}\" وجميع محتوياته إلى سلة المهملات؟",
|
||||
"confirm_permanent_delete": "حذف نهائي",
|
||||
"confirm_permanent_delete_msg": "هل أنت متأكد أنك تريد حذف هذا العنصر نهائياً؟ لا يمكن التراجع عن هذا الإجراء.",
|
||||
"confirm_empty_trash": "تفريغ سلة المهملات",
|
||||
"confirm_delete_share": "حذف رابط المشاركة",
|
||||
"confirm_delete_share_msg": "هل أنت متأكد أنك تريد حذف رابط المشاركة هذا؟",
|
||||
"share_file": "مشاركة الملف",
|
||||
"existing_shares": "المشاركات الحالية",
|
||||
"share_options": "خيارات المشاركة",
|
||||
"password": "كلمة المرور",
|
||||
"expiration": "انتهاء الصلاحية",
|
||||
"permissions": "الصلاحيات",
|
||||
"generated_link": "الرابط المُنشأ",
|
||||
"notify": "إرسال إشعار",
|
||||
"recipient": "المستلم",
|
||||
"message": "الرسالة"
|
||||
},
|
||||
"dropzone": {
|
||||
"drag_files": "اسحب الملفات هنا أو انقر للاختيار",
|
||||
"drop_files": "أسقط الملفات للرفع"
|
||||
},
|
||||
"permissions": {
|
||||
"read": "قراءة",
|
||||
"write": "كتابة",
|
||||
"reshare": "إعادة مشاركة"
|
||||
},
|
||||
"errors": {
|
||||
"file_not_found": "الملف غير موجود",
|
||||
"folder_not_found": "المجلد غير موجود",
|
||||
"delete_error": "خطأ في الحذف",
|
||||
"upload_error": "خطأ في رفع الملف",
|
||||
"rename_error": "خطأ في إعادة التسمية",
|
||||
"move_error": "خطأ في النقل",
|
||||
"empty_name": "لا يمكن أن يكون الاسم فارغاً",
|
||||
"name_exists": "ملف أو مجلد بهذا الاسم موجود بالفعل",
|
||||
"generic_error": "حدث خطأ"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"home": "الرئيسية"
|
||||
},
|
||||
"trash": {
|
||||
"empty_trash": "تفريغ سلة المهملات",
|
||||
"empty_state": "سلة المهملات فارغة",
|
||||
"original_location": "الموقع الأصلي",
|
||||
"deleted_date": "تاريخ الحذف",
|
||||
"actions": "الإجراءات",
|
||||
"restore": "استعادة",
|
||||
"delete_permanently": "حذف نهائياً",
|
||||
"empty_confirm": "هل أنت متأكد أنك تريد تفريغ سلة المهملات؟ سيتم حذف جميع العناصر نهائياً."
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "تسجيل الدخول",
|
||||
"username": "اسم المستخدم",
|
||||
"username_placeholder": "أدخل اسم المستخدم",
|
||||
"password": "كلمة المرور",
|
||||
"password_placeholder": "أدخل كلمة المرور",
|
||||
"login_button": "تسجيل الدخول",
|
||||
"no_account": "ليس لديك حساب؟",
|
||||
"register": "إنشاء حساب",
|
||||
"admin_setup": "أول مرة؟",
|
||||
"setup": "إعداد المسؤول",
|
||||
"register_title": "إنشاء حساب",
|
||||
"email": "البريد الإلكتروني",
|
||||
"email_placeholder": "أدخل بريدك الإلكتروني",
|
||||
"confirm_password": "تأكيد كلمة المرور",
|
||||
"confirm_password_placeholder": "أكد كلمة المرور",
|
||||
"register_button": "إنشاء حساب",
|
||||
"have_account": "لديك حساب بالفعل؟",
|
||||
"login": "تسجيل الدخول",
|
||||
"setup_title": "الإعداد الأولي",
|
||||
"setup_step1": "المسؤول",
|
||||
"setup_step2": "النظام",
|
||||
"setup_step3": "مكتمل",
|
||||
"admin_username": "اسم مستخدم المسؤول",
|
||||
"admin_email": "بريد المسؤول الإلكتروني",
|
||||
"admin_password": "كلمة مرور المسؤول",
|
||||
"create_admin": "إنشاء حساب المسؤول",
|
||||
"back_to_login": "تم الإعداد مسبقاً؟",
|
||||
"admin_success": "تم إنشاء حساب المسؤول بنجاح! يمكنك الآن تسجيل الدخول.",
|
||||
"account_success": "تم إنشاء الحساب بنجاح! يمكنك الآن تسجيل الدخول.",
|
||||
"passwords_mismatch": "كلمات المرور غير متطابقة",
|
||||
"admin_create_error": "خطأ في إنشاء حساب المسؤول",
|
||||
"or": "أو",
|
||||
"sso_login": "تسجيل الدخول عبر SSO",
|
||||
"sso_login_provider": "تسجيل الدخول عبر {{provider}}"
|
||||
},
|
||||
"storage": {
|
||||
"title": "التخزين",
|
||||
"calculating": "جارٍ الحساب...",
|
||||
"used": "{{percentage}}% مستخدم ({{used}} / {{total}})"
|
||||
},
|
||||
"viewer": {
|
||||
"unsupported_file": "لا يمكن معاينة هذا النوع من الملفات.",
|
||||
"download_file": "تحميل الملف",
|
||||
"zoom_in": "تكبير",
|
||||
"zoom_out": "تصغير",
|
||||
"zoom_reset": "إعادة تعيين التكبير"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "!مرحباً",
|
||||
"subtitle": "اختر لغتك للمتابعة",
|
||||
"continue": "متابعة",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"zh": "中文",
|
||||
"fa": "فارسی",
|
||||
"fr": "Français",
|
||||
"de": "Deutsch",
|
||||
"pt": "Português",
|
||||
"ar": "العربية"
|
||||
}
|
||||
},
|
||||
"favorites": {
|
||||
"empty_state": "لا توجد مفضلات بعد",
|
||||
"empty_hint": "ضع نجمة على الملفات أو المجلدات لإضافتها إلى المفضلة",
|
||||
"add": "إضافة للمفضلة",
|
||||
"remove": "إزالة من المفضلة",
|
||||
"added_title": "أُضيف للمفضلة",
|
||||
"added_msg": "أُضيف للمفضلة",
|
||||
"removed_title": "أُزيل من المفضلة",
|
||||
"removed_msg": "أُزيل من المفضلة"
|
||||
},
|
||||
"recent": {
|
||||
"title": "الأخيرة",
|
||||
"clear": "مسح الأخيرة",
|
||||
"accessed": "تم الوصول",
|
||||
"empty_state": "لا توجد ملفات حديثة",
|
||||
"empty_hint": "الملفات التي تفتحها ستظهر هنا"
|
||||
},
|
||||
"notifications": {
|
||||
"file_renamed": "تمت إعادة تسمية الملف",
|
||||
"file_renamed_to": "تمت إعادة تسمية الملف إلى \"{{name}}\"",
|
||||
"folder_renamed": "تمت إعادة تسمية المجلد",
|
||||
"folder_renamed_to": "تمت إعادة تسمية المجلد إلى \"{{name}}\"",
|
||||
"file_uploaded": "تم رفع الملف",
|
||||
"file_deleted": "تم نقل الملف إلى سلة المهملات",
|
||||
"folder_deleted": "تم نقل المجلد إلى سلة المهملات",
|
||||
"item_deleted_permanently": "تم حذف العنصر نهائياً",
|
||||
"trash_emptied": "تم تفريغ سلة المهملات بنجاح",
|
||||
"title": "الإشعارات",
|
||||
"empty": "لا توجد إشعارات"
|
||||
},
|
||||
"batch": {
|
||||
"one_selected": "عنصر واحد محدد",
|
||||
"n_selected": "{{count}} عناصر محددة",
|
||||
"confirm_delete": "هل أنت متأكد أنك تريد نقل {{count}} عنصر إلى سلة المهملات؟",
|
||||
"move_title": "نقل {{count}} عنصر",
|
||||
"add_favorites": "إضافة للمفضلة",
|
||||
"move_copy": "نقل أو نسخ"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "لوحة الإدارة",
|
||||
"back_to_app": "العودة إلى OxiCloud",
|
||||
"loading": "جارٍ التحميل…",
|
||||
"access_denied": "الوصول مرفوض",
|
||||
"access_denied_desc": "صلاحيات المسؤول مطلوبة.",
|
||||
"sign_in": "تسجيل الدخول",
|
||||
"tab_dashboard": "لوحة المعلومات",
|
||||
"tab_users": "المستخدمون",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "إجمالي المستخدمين",
|
||||
"active_users": "المستخدمون النشطون",
|
||||
"admins": "المسؤولون",
|
||||
"version": "الإصدار",
|
||||
"storage_overview": "نظرة عامة على التخزين",
|
||||
"used": "مستخدم",
|
||||
"total_quota": "الحصة الإجمالية",
|
||||
"usage_pct": "نسبة الاستخدام",
|
||||
"users_over_80": "مستخدمون >80%",
|
||||
"users_over_quota": "مستخدمون تجاوزوا الحصة",
|
||||
"system": "النظام",
|
||||
"auth_label": "المصادقة",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "الحصص",
|
||||
"enabled": "مفعّل",
|
||||
"disabled": "معطّل",
|
||||
"active": "نشط",
|
||||
"off": "متوقف",
|
||||
"allow_registration": "السماح بالتسجيل العام",
|
||||
"registration_warning": "التسجيل العام معطّل. فقط المسؤولون يمكنهم إنشاء مستخدمين.",
|
||||
"user_management": "إدارة المستخدمين",
|
||||
"create_user": "إنشاء مستخدم",
|
||||
"col_user": "المستخدم",
|
||||
"col_role": "الدور",
|
||||
"col_auth": "المصادقة",
|
||||
"col_status": "الحالة",
|
||||
"col_storage": "التخزين",
|
||||
"col_last_login": "آخر دخول",
|
||||
"col_actions": "الإجراءات",
|
||||
"loading_users": "جارٍ تحميل المستخدمين…",
|
||||
"failed_load_users": "فشل التحميل",
|
||||
"no_users_found": "لم يتم العثور على مستخدمين",
|
||||
"showing_users": "عرض {{from}}-{{to}} من {{total}}",
|
||||
"prev": "السابق",
|
||||
"next": "التالي",
|
||||
"inactive": "غير نشط",
|
||||
"you_badge": "(أنت)",
|
||||
"local": "محلي",
|
||||
"never": "أبداً",
|
||||
"just_now": "الآن",
|
||||
"minutes_ago": "منذ {{n}} دقيقة",
|
||||
"hours_ago": "منذ {{n}} ساعة",
|
||||
"days_ago": "منذ {{n}} يوم",
|
||||
"edit_quota_title": "تعديل الحصة",
|
||||
"reset_password_title": "إعادة تعيين كلمة المرور",
|
||||
"toggle_role_title": "تبديل الدور",
|
||||
"deactivate_title": "تعطيل",
|
||||
"activate_title": "تفعيل",
|
||||
"delete_title": "حذف",
|
||||
"sso_title": "تسجيل الدخول الموحد (OIDC / SSO)",
|
||||
"enable_sso": "تفعيل مصادقة SSO",
|
||||
"provider_name": "اسم الموفر",
|
||||
"issuer_url": "عنوان المُصدر",
|
||||
"issuer_url_hint": "عنوان مُصدر OpenID Connect",
|
||||
"auto_discover": "اكتشاف تلقائي",
|
||||
"discovering": "جارٍ الاكتشاف…",
|
||||
"client_id": "معرّف العميل",
|
||||
"client_secret": "سر العميل",
|
||||
"client_secret_placeholder": "اتركه فارغاً للاحتفاظ بالقيمة",
|
||||
"secret_configured": "سر العميل مُهيأ بالفعل",
|
||||
"callback_url": "عنوان الاستدعاء",
|
||||
"callback_url_hint": "(سجّل في IdP)",
|
||||
"advanced_settings": "إعدادات متقدمة",
|
||||
"scopes": "النطاقات",
|
||||
"auto_provision": "إنشاء تلقائي عند أول دخول",
|
||||
"admin_groups": "مجموعات المسؤولين",
|
||||
"admin_groups_hint": "أسماء مجموعات OIDC مفصولة بفواصل",
|
||||
"disable_password": "تعطيل الدخول بكلمة المرور (OIDC فقط)",
|
||||
"password_warning": "سيمنع جميع عمليات الدخول بالمرور!",
|
||||
"test_btn": "اختبار",
|
||||
"save_btn": "حفظ",
|
||||
"saving": "جارٍ الحفظ…",
|
||||
"settings_saved": "تم الحفظ — OIDC الآن {{status}}",
|
||||
"quota_modal_title": "تحديث حصة التخزين",
|
||||
"quota_user_label": "المستخدم:",
|
||||
"new_quota": "حصة جديدة",
|
||||
"quota_unlimited_hint": "0 لغير محدود",
|
||||
"cancel": "إلغاء",
|
||||
"create_user_title": "إنشاء مستخدم جديد",
|
||||
"username_label": "اسم المستخدم",
|
||||
"username_placeholder": "اسم_المستخدم",
|
||||
"username_hint": "3–32 حرفاً",
|
||||
"password_label": "كلمة المرور",
|
||||
"password_placeholder": "8 أحرف على الأقل",
|
||||
"email_label": "البريد",
|
||||
"email_optional": "(اختياري)",
|
||||
"email_placeholder": "user@example.com (يُنشأ تلقائياً)",
|
||||
"role_label": "الدور",
|
||||
"role_user": "مستخدم",
|
||||
"role_admin": "مسؤول",
|
||||
"quota_label": "الحصة",
|
||||
"creating": "جارٍ الإنشاء…",
|
||||
"reset_pw_title": "إعادة تعيين كلمة المرور",
|
||||
"new_password_label": "كلمة مرور جديدة",
|
||||
"resetting": "جارٍ إعادة التعيين…",
|
||||
"reset_btn": "إعادة تعيين",
|
||||
"confirm_role_change": "تغيير الدور إلى {{role}}؟",
|
||||
"confirm_deactivate": "هل أنت متأكد من التعطيل؟",
|
||||
"confirm_activate": "هل أنت متأكد من التفعيل؟",
|
||||
"confirm_delete_user": "حذف المستخدم \"{{name}}\"؟ لا يمكن التراجع!",
|
||||
"confirm_action": "تأكيد الإجراء",
|
||||
"confirm_yes": "تأكيد",
|
||||
"confirm_no": "إلغاء",
|
||||
"error_username_short": "الاسم 3 أحرف على الأقل",
|
||||
"error_password_short": "كلمة المرور 8 أحرف على الأقل",
|
||||
"error_generic": "فشل",
|
||||
"error_network": "خطأ في الشبكة: {{message}}",
|
||||
"error_create_user": "فشل إنشاء المستخدم"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "الملف الشخصي",
|
||||
"back_to_app": "العودة إلى OxiCloud",
|
||||
"loading": "جارٍ التحميل…",
|
||||
"not_authenticated": "غير مُصادق",
|
||||
"not_authenticated_desc": "سجّل الدخول لعرض ملفك الشخصي.",
|
||||
"sign_in": "تسجيل الدخول",
|
||||
"role_admin": "مسؤول",
|
||||
"role_user": "مستخدم",
|
||||
"account_details": "تفاصيل الحساب",
|
||||
"username": "اسم المستخدم",
|
||||
"email": "البريد الإلكتروني",
|
||||
"role": "الدور",
|
||||
"last_login": "آخر دخول",
|
||||
"storage": "التخزين",
|
||||
"used": "مستخدم",
|
||||
"quota": "الحصة",
|
||||
"usage": "الاستخدام",
|
||||
"unlimited": "غير محدود",
|
||||
"app_passwords": "كلمات مرور التطبيقات",
|
||||
"app_pw_desc": "أنشئ كلمات مرور لعملاء WebDAV و CalDAV و CardDAV. تُعرض كل كلمة مرور مرة واحدة فقط.",
|
||||
"app_pw_label_placeholder": "التسمية (مثلاً Thunderbird، macOS)",
|
||||
"generate": "إنشاء",
|
||||
"generating": "جارٍ الإنشاء…",
|
||||
"new_password_for": "كلمة مرور جديدة لـ",
|
||||
"copy_warning": "انسخ كلمة المرور الآن. لن تتمكن من رؤيتها مرة أخرى.",
|
||||
"copy_to_clipboard": "نسخ إلى الحافظة",
|
||||
"col_label": "التسمية",
|
||||
"col_created": "تاريخ الإنشاء",
|
||||
"col_last_used": "آخر استخدام",
|
||||
"col_status": "الحالة",
|
||||
"active": "نشط",
|
||||
"revoked": "ملغى",
|
||||
"revoke_title": "إلغاء",
|
||||
"no_app_passwords": "لا توجد كلمات مرور تطبيقات بعد.",
|
||||
"client_sessions": "جلسات العميل",
|
||||
"client_sessions_desc": "تُنشأ تلقائيًا عند اتصال عميل متوافق مع Nextcloud.",
|
||||
"col_client": "العميل",
|
||||
"never": "أبداً",
|
||||
"just_now": "الآن",
|
||||
"minutes_ago": "منذ {{n}} دقيقة",
|
||||
"hours_ago": "منذ {{n}} ساعة",
|
||||
"days_ago": "منذ {{n}} يوم",
|
||||
"change_password": "تغيير كلمة المرور",
|
||||
"current_password": "كلمة المرور الحالية",
|
||||
"new_password": "كلمة المرور الجديدة",
|
||||
"min_8_chars": "8 أحرف على الأقل",
|
||||
"confirm_password": "تأكيد كلمة المرور الجديدة",
|
||||
"update_password": "تحديث كلمة المرور",
|
||||
"updating": "جارٍ التحديث…",
|
||||
"password_updated": "تم تحديث كلمة المرور بنجاح",
|
||||
"passwords_no_match": "كلمتا المرور غير متطابقتين",
|
||||
"password_too_short": "يجب أن تكون كلمة المرور 8 أحرف على الأقل",
|
||||
"password_change_failed": "فشل تغيير كلمة المرور",
|
||||
"error_network": "خطأ في الشبكة: {{message}}",
|
||||
"error_label_required": "أدخل تسمية",
|
||||
"error_create_pw": "فشل إنشاء كلمة المرور",
|
||||
"confirm_revoke": "إلغاء كلمة المرور \"{{label}}\"؟ ستتوقف العملاء عن العمل.",
|
||||
"error_revoke": "فشل الإلغاء"
|
||||
}
|
||||
}
|
||||
+182
-2
@@ -182,6 +182,7 @@
|
||||
"size": "Größe",
|
||||
"modified": "Geändert",
|
||||
"no_files": "Keine Dateien in diesem Ordner",
|
||||
"empty_hint": "Laden Sie Dateien hoch oder erstellen Sie Ordner, um loszulegen",
|
||||
"loading": "Dateien werden geladen…",
|
||||
"view_grid": "Rasteransicht",
|
||||
"view_list": "Listenansicht",
|
||||
@@ -317,8 +318,8 @@
|
||||
"zoom_reset": "Zoom zurücksetzen"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "Willkommen bei OxiCloud",
|
||||
"subtitle": "Bitte wählen Sie Ihre Sprache",
|
||||
"title": "Willkommen!",
|
||||
"subtitle": "Wählen Sie Ihre Sprache, um fortzufahren",
|
||||
"continue": "Weiter",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -367,5 +368,184 @@
|
||||
"move_title": "{{count}} Element(e) verschieben",
|
||||
"add_favorites": "Zu Favoriten hinzufügen",
|
||||
"move_copy": "Verschieben oder kopieren"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "Admin-Panel",
|
||||
"back_to_app": "Zurück zu OxiCloud",
|
||||
"loading": "Laden…",
|
||||
"access_denied": "Zugriff verweigert",
|
||||
"access_denied_desc": "Administratorrechte erforderlich.",
|
||||
"sign_in": "Anmelden",
|
||||
"tab_dashboard": "Dashboard",
|
||||
"tab_users": "Benutzer",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "Benutzer gesamt",
|
||||
"active_users": "Aktive Benutzer",
|
||||
"admins": "Admins",
|
||||
"version": "Version",
|
||||
"storage_overview": "Speicherübersicht",
|
||||
"used": "Verwendet",
|
||||
"total_quota": "Gesamtkontingent",
|
||||
"usage_pct": "Nutzung %",
|
||||
"users_over_80": "Benutzer >80% Kontingent",
|
||||
"users_over_quota": "Benutzer über Kontingent",
|
||||
"system": "System",
|
||||
"auth_label": "Auth",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "Kontingente",
|
||||
"enabled": "Aktiviert",
|
||||
"disabled": "Deaktiviert",
|
||||
"active": "Aktiv",
|
||||
"off": "Aus",
|
||||
"allow_registration": "Öffentliche Selbstregistrierung erlauben",
|
||||
"registration_warning": "Öffentliche Registrierung ist deaktiviert. Nur Admins können neue Benutzer erstellen.",
|
||||
"user_management": "Benutzerverwaltung",
|
||||
"create_user": "Benutzer erstellen",
|
||||
"col_user": "Benutzer",
|
||||
"col_role": "Rolle",
|
||||
"col_auth": "Auth",
|
||||
"col_status": "Status",
|
||||
"col_storage": "Speicher",
|
||||
"col_last_login": "Letzter Login",
|
||||
"col_actions": "Aktionen",
|
||||
"loading_users": "Benutzer werden geladen…",
|
||||
"failed_load_users": "Laden fehlgeschlagen",
|
||||
"no_users_found": "Keine Benutzer gefunden",
|
||||
"showing_users": "Zeige {{from}}-{{to}} von {{total}}",
|
||||
"prev": "Zurück",
|
||||
"next": "Weiter",
|
||||
"inactive": "Inaktiv",
|
||||
"you_badge": "(du)",
|
||||
"local": "Lokal",
|
||||
"never": "Nie",
|
||||
"just_now": "Gerade eben",
|
||||
"minutes_ago": "vor {{n}}Min",
|
||||
"hours_ago": "vor {{n}}Std",
|
||||
"days_ago": "vor {{n}}T",
|
||||
"edit_quota_title": "Kontingent bearbeiten",
|
||||
"reset_password_title": "Passwort zurücksetzen",
|
||||
"toggle_role_title": "Rolle wechseln",
|
||||
"deactivate_title": "Deaktivieren",
|
||||
"activate_title": "Aktivieren",
|
||||
"delete_title": "Löschen",
|
||||
"sso_title": "Single Sign-On (OIDC / SSO)",
|
||||
"enable_sso": "SSO-Authentifizierung aktivieren",
|
||||
"provider_name": "Anbietername",
|
||||
"issuer_url": "Aussteller-URL",
|
||||
"issuer_url_hint": "OpenID Connect Aussteller-URL Ihres Identitätsanbieters",
|
||||
"auto_discover": "Auto-Erkennung",
|
||||
"discovering": "Erkennung…",
|
||||
"client_id": "Client-ID",
|
||||
"client_secret": "Client-Secret",
|
||||
"client_secret_placeholder": "Leer lassen für aktuellen Wert",
|
||||
"secret_configured": "Ein Client-Secret ist bereits konfiguriert",
|
||||
"callback_url": "Callback-URL",
|
||||
"callback_url_hint": "(bei IdP registrieren)",
|
||||
"advanced_settings": "Erweiterte Einstellungen",
|
||||
"scopes": "Scopes",
|
||||
"auto_provision": "Benutzer bei erstem Login automatisch anlegen",
|
||||
"admin_groups": "Admin-Gruppen",
|
||||
"admin_groups_hint": "Kommagetrennte OIDC-Gruppennamen für Admin-Rolle",
|
||||
"disable_password": "Passwort-Login deaktivieren (nur OIDC)",
|
||||
"password_warning": "Dies verhindert ALLE passwortbasierten Anmeldungen!",
|
||||
"test_btn": "Testen",
|
||||
"save_btn": "Speichern",
|
||||
"saving": "Speichern…",
|
||||
"settings_saved": "Einstellungen gespeichert — OIDC ist jetzt {{status}}",
|
||||
"quota_modal_title": "Speicherkontingent aktualisieren",
|
||||
"quota_user_label": "Benutzer:",
|
||||
"new_quota": "Neues Kontingent",
|
||||
"quota_unlimited_hint": "0 für unbegrenzt",
|
||||
"cancel": "Abbrechen",
|
||||
"create_user_title": "Neuen Benutzer erstellen",
|
||||
"username_label": "Benutzername",
|
||||
"username_placeholder": "maxmuster",
|
||||
"username_hint": "3–32 Zeichen",
|
||||
"password_label": "Passwort",
|
||||
"password_placeholder": "Min. 8 Zeichen",
|
||||
"email_label": "E-Mail",
|
||||
"email_optional": "(optional)",
|
||||
"email_placeholder": "benutzer@beispiel.de (automatisch wenn leer)",
|
||||
"role_label": "Rolle",
|
||||
"role_user": "Benutzer",
|
||||
"role_admin": "Admin",
|
||||
"quota_label": "Kontingent",
|
||||
"creating": "Erstellen…",
|
||||
"reset_pw_title": "Passwort zurücksetzen",
|
||||
"new_password_label": "Neues Passwort",
|
||||
"resetting": "Zurücksetzen…",
|
||||
"reset_btn": "Zurücksetzen",
|
||||
"confirm_role_change": "Rolle zu {{role}} ändern?",
|
||||
"confirm_deactivate": "Diesen Benutzer wirklich deaktivieren?",
|
||||
"confirm_activate": "Diesen Benutzer wirklich aktivieren?",
|
||||
"confirm_delete_user": "Benutzer \"{{name}}\" LÖSCHEN? Kann nicht rückgängig gemacht werden!",
|
||||
"confirm_action": "Aktion bestätigen",
|
||||
"confirm_yes": "Bestätigen",
|
||||
"confirm_no": "Abbrechen",
|
||||
"error_username_short": "Benutzername muss mindestens 3 Zeichen haben",
|
||||
"error_password_short": "Passwort muss mindestens 8 Zeichen haben",
|
||||
"error_generic": "Fehlgeschlagen",
|
||||
"error_network": "Netzwerkfehler: {{message}}",
|
||||
"error_create_user": "Benutzer erstellen fehlgeschlagen"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "Profil",
|
||||
"back_to_app": "Zurück zu OxiCloud",
|
||||
"loading": "Laden…",
|
||||
"not_authenticated": "Nicht authentifiziert",
|
||||
"not_authenticated_desc": "Bitte melden Sie sich an, um Ihr Profil anzuzeigen.",
|
||||
"sign_in": "Anmelden",
|
||||
"role_admin": "Administrator",
|
||||
"role_user": "Benutzer",
|
||||
"account_details": "Kontodetails",
|
||||
"username": "Benutzername",
|
||||
"email": "E-Mail",
|
||||
"role": "Rolle",
|
||||
"last_login": "Letzter Login",
|
||||
"storage": "Speicher",
|
||||
"used": "Verwendet",
|
||||
"quota": "Kontingent",
|
||||
"usage": "Nutzung",
|
||||
"unlimited": "Unbegrenzt",
|
||||
"app_passwords": "App-Passwörter",
|
||||
"app_pw_desc": "Passwörter für WebDAV-, CalDAV- und CardDAV-Clients generieren. Jedes Passwort wird nur einmal angezeigt.",
|
||||
"app_pw_label_placeholder": "Bezeichnung (z.B. Thunderbird, macOS)",
|
||||
"generate": "Generieren",
|
||||
"generating": "Generieren…",
|
||||
"new_password_for": "Neues Passwort für",
|
||||
"copy_warning": "Kopieren Sie dieses Passwort jetzt. Sie können es nicht erneut anzeigen.",
|
||||
"copy_to_clipboard": "In Zwischenablage kopieren",
|
||||
"col_label": "Bezeichnung",
|
||||
"col_created": "Erstellt",
|
||||
"col_last_used": "Zuletzt verwendet",
|
||||
"col_status": "Status",
|
||||
"active": "Aktiv",
|
||||
"revoked": "Widerrufen",
|
||||
"revoke_title": "Widerrufen",
|
||||
"no_app_passwords": "Noch keine App-Passwörter.",
|
||||
"client_sessions": "Client-Sitzungen",
|
||||
"client_sessions_desc": "Automatisch generiert beim Verbinden eines Nextcloud-kompatiblen Clients.",
|
||||
"col_client": "Client",
|
||||
"never": "Nie",
|
||||
"just_now": "Gerade eben",
|
||||
"minutes_ago": "vor {{n}} Min",
|
||||
"hours_ago": "vor {{n}} Std",
|
||||
"days_ago": "vor {{n}} Tagen",
|
||||
"change_password": "Passwort ändern",
|
||||
"current_password": "Aktuelles Passwort",
|
||||
"new_password": "Neues Passwort",
|
||||
"min_8_chars": "Mindestens 8 Zeichen",
|
||||
"confirm_password": "Neues Passwort bestätigen",
|
||||
"update_password": "Passwort aktualisieren",
|
||||
"updating": "Aktualisierung…",
|
||||
"password_updated": "Passwort erfolgreich aktualisiert",
|
||||
"passwords_no_match": "Passwörter stimmen nicht überein",
|
||||
"password_too_short": "Passwort muss mindestens 8 Zeichen haben",
|
||||
"password_change_failed": "Passwort ändern fehlgeschlagen",
|
||||
"error_network": "Netzwerkfehler: {{message}}",
|
||||
"error_label_required": "Bitte Bezeichnung eingeben",
|
||||
"error_create_pw": "App-Passwort erstellen fehlgeschlagen",
|
||||
"confirm_revoke": "App-Passwort \"{{label}}\" widerrufen? Clients werden nicht mehr funktionieren.",
|
||||
"error_revoke": "Widerrufen fehlgeschlagen"
|
||||
}
|
||||
}
|
||||
|
||||
+182
-2
@@ -182,6 +182,7 @@
|
||||
"size": "Size",
|
||||
"modified": "Modified",
|
||||
"no_files": "No files in this folder",
|
||||
"empty_hint": "Upload files or create folders to get started",
|
||||
"loading": "Loading files…",
|
||||
"view_grid": "Grid view",
|
||||
"view_list": "List view",
|
||||
@@ -320,8 +321,8 @@
|
||||
"zoom_reset": "Reset zoom"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "Welcome to OxiCloud",
|
||||
"subtitle": "Please select your language",
|
||||
"title": "Welcome!",
|
||||
"subtitle": "Select your language to continue",
|
||||
"continue": "Continue",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -370,5 +371,184 @@
|
||||
"move_title": "Move {{count}} item(s)",
|
||||
"add_favorites": "Add to favorites",
|
||||
"move_copy": "Move or copy"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "Admin Panel",
|
||||
"back_to_app": "Back to OxiCloud",
|
||||
"loading": "Loading…",
|
||||
"access_denied": "Access Denied",
|
||||
"access_denied_desc": "Administrator privileges required to access this panel.",
|
||||
"sign_in": "Sign in",
|
||||
"tab_dashboard": "Dashboard",
|
||||
"tab_users": "Users",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "Total Users",
|
||||
"active_users": "Active Users",
|
||||
"admins": "Admins",
|
||||
"version": "Version",
|
||||
"storage_overview": "Storage Overview",
|
||||
"used": "Used",
|
||||
"total_quota": "Total Quota",
|
||||
"usage_pct": "Usage %",
|
||||
"users_over_80": "Users >80% quota",
|
||||
"users_over_quota": "Users over quota",
|
||||
"system": "System",
|
||||
"auth_label": "Auth",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "Quotas",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"active": "Active",
|
||||
"off": "Off",
|
||||
"allow_registration": "Allow public self-registration",
|
||||
"registration_warning": "Public registration is disabled. Only admins can create new users.",
|
||||
"user_management": "User Management",
|
||||
"create_user": "Create User",
|
||||
"col_user": "User",
|
||||
"col_role": "Role",
|
||||
"col_auth": "Auth",
|
||||
"col_status": "Status",
|
||||
"col_storage": "Storage",
|
||||
"col_last_login": "Last Login",
|
||||
"col_actions": "Actions",
|
||||
"loading_users": "Loading users…",
|
||||
"failed_load_users": "Failed to load users",
|
||||
"no_users_found": "No users found",
|
||||
"showing_users": "Showing {{from}}-{{to}} of {{total}}",
|
||||
"prev": "Prev",
|
||||
"next": "Next",
|
||||
"inactive": "Inactive",
|
||||
"you_badge": "(you)",
|
||||
"local": "Local",
|
||||
"never": "Never",
|
||||
"just_now": "Just now",
|
||||
"minutes_ago": "{{n}}m ago",
|
||||
"hours_ago": "{{n}}h ago",
|
||||
"days_ago": "{{n}}d ago",
|
||||
"edit_quota_title": "Edit quota",
|
||||
"reset_password_title": "Reset password",
|
||||
"toggle_role_title": "Toggle role",
|
||||
"deactivate_title": "Deactivate",
|
||||
"activate_title": "Activate",
|
||||
"delete_title": "Delete",
|
||||
"sso_title": "Single Sign-On (OIDC / SSO)",
|
||||
"enable_sso": "Enable SSO Authentication",
|
||||
"provider_name": "Provider Name",
|
||||
"issuer_url": "Issuer URL",
|
||||
"issuer_url_hint": "OpenID Connect issuer URL of your identity provider",
|
||||
"auto_discover": "Auto-discover",
|
||||
"discovering": "Discovering…",
|
||||
"client_id": "Client ID",
|
||||
"client_secret": "Client Secret",
|
||||
"client_secret_placeholder": "Leave empty to keep current value",
|
||||
"secret_configured": "A client secret is already configured",
|
||||
"callback_url": "Callback URL",
|
||||
"callback_url_hint": "(register in your IdP)",
|
||||
"advanced_settings": "Advanced Settings",
|
||||
"scopes": "Scopes",
|
||||
"auto_provision": "Auto-provision users on first login",
|
||||
"admin_groups": "Admin Groups",
|
||||
"admin_groups_hint": "Comma-separated OIDC group names that map to admin role",
|
||||
"disable_password": "Disable password login (OIDC only)",
|
||||
"password_warning": "This will prevent ALL password-based logins!",
|
||||
"test_btn": "Test",
|
||||
"save_btn": "Save",
|
||||
"saving": "Saving…",
|
||||
"settings_saved": "Settings saved — OIDC is now {{status}}",
|
||||
"quota_modal_title": "Update Storage Quota",
|
||||
"quota_user_label": "User:",
|
||||
"new_quota": "New Quota",
|
||||
"quota_unlimited_hint": "Set to 0 for unlimited",
|
||||
"cancel": "Cancel",
|
||||
"create_user_title": "Create New User",
|
||||
"username_label": "Username",
|
||||
"username_placeholder": "johndoe",
|
||||
"username_hint": "3–32 characters",
|
||||
"password_label": "Password",
|
||||
"password_placeholder": "Min 8 characters",
|
||||
"email_label": "Email",
|
||||
"email_optional": "(optional)",
|
||||
"email_placeholder": "user@example.com (auto-generated if empty)",
|
||||
"role_label": "Role",
|
||||
"role_user": "User",
|
||||
"role_admin": "Admin",
|
||||
"quota_label": "Quota",
|
||||
"creating": "Creating…",
|
||||
"reset_pw_title": "Reset Password",
|
||||
"new_password_label": "New Password",
|
||||
"resetting": "Resetting…",
|
||||
"reset_btn": "Reset",
|
||||
"confirm_role_change": "Change role to {{role}}?",
|
||||
"confirm_deactivate": "Are you sure you want to deactivate this user?",
|
||||
"confirm_activate": "Are you sure you want to activate this user?",
|
||||
"confirm_delete_user": "DELETE user \"{{name}}\"? This cannot be undone!",
|
||||
"confirm_action": "Confirm Action",
|
||||
"confirm_yes": "Confirm",
|
||||
"confirm_no": "Cancel",
|
||||
"error_username_short": "Username must be at least 3 characters",
|
||||
"error_password_short": "Password must be at least 8 characters",
|
||||
"error_generic": "Failed",
|
||||
"error_network": "Network error: {{message}}",
|
||||
"error_create_user": "Failed to create user"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "Profile",
|
||||
"back_to_app": "Back to OxiCloud",
|
||||
"loading": "Loading…",
|
||||
"not_authenticated": "Not Authenticated",
|
||||
"not_authenticated_desc": "Please sign in to view your profile.",
|
||||
"sign_in": "Sign in",
|
||||
"role_admin": "Administrator",
|
||||
"role_user": "User",
|
||||
"account_details": "Account Details",
|
||||
"username": "Username",
|
||||
"email": "Email",
|
||||
"role": "Role",
|
||||
"last_login": "Last Login",
|
||||
"storage": "Storage",
|
||||
"used": "Used",
|
||||
"quota": "Quota",
|
||||
"usage": "Usage",
|
||||
"unlimited": "Unlimited",
|
||||
"app_passwords": "App Passwords",
|
||||
"app_pw_desc": "Generate passwords for WebDAV, CalDAV, and CardDAV clients. Each password is shown only once.",
|
||||
"app_pw_label_placeholder": "Label (e.g. Thunderbird, macOS)",
|
||||
"generate": "Generate",
|
||||
"generating": "Generating…",
|
||||
"new_password_for": "New password for",
|
||||
"copy_warning": "Copy this password now. You won't be able to see it again.",
|
||||
"copy_to_clipboard": "Copy to clipboard",
|
||||
"col_label": "Label",
|
||||
"col_created": "Created",
|
||||
"col_last_used": "Last Used",
|
||||
"col_status": "Status",
|
||||
"active": "Active",
|
||||
"revoked": "Revoked",
|
||||
"revoke_title": "Revoke",
|
||||
"no_app_passwords": "No app passwords yet.",
|
||||
"client_sessions": "Client sessions",
|
||||
"client_sessions_desc": "Auto-generated when you connect a Nextcloud-compatible client.",
|
||||
"col_client": "Client",
|
||||
"never": "Never",
|
||||
"just_now": "Just now",
|
||||
"minutes_ago": "{{n}} min ago",
|
||||
"hours_ago": "{{n}}h ago",
|
||||
"days_ago": "{{n}} days ago",
|
||||
"change_password": "Change Password",
|
||||
"current_password": "Current Password",
|
||||
"new_password": "New Password",
|
||||
"min_8_chars": "At least 8 characters",
|
||||
"confirm_password": "Confirm New Password",
|
||||
"update_password": "Update Password",
|
||||
"updating": "Updating…",
|
||||
"password_updated": "Password updated successfully",
|
||||
"passwords_no_match": "Passwords do not match",
|
||||
"password_too_short": "Password must be at least 8 characters",
|
||||
"password_change_failed": "Failed to change password",
|
||||
"error_network": "Network error: {{message}}",
|
||||
"error_label_required": "Please enter a label",
|
||||
"error_create_pw": "Failed to create app password",
|
||||
"confirm_revoke": "Revoke app password \"{{label}}\"? Clients using this password will stop working.",
|
||||
"error_revoke": "Failed to revoke app password"
|
||||
}
|
||||
}
|
||||
+182
-2
@@ -182,6 +182,7 @@
|
||||
"size": "Tamaño",
|
||||
"modified": "Modificado",
|
||||
"no_files": "No hay archivos en esta carpeta",
|
||||
"empty_hint": "Sube archivos o crea carpetas para comenzar",
|
||||
"loading": "Cargando archivos…",
|
||||
"view_grid": "Vista de cuadrícula",
|
||||
"view_list": "Vista de lista",
|
||||
@@ -320,8 +321,8 @@
|
||||
"zoom_reset": "Restablecer zoom"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "Bienvenido a OxiCloud",
|
||||
"subtitle": "Por favor, selecciona tu idioma",
|
||||
"title": "¡Bienvenido!",
|
||||
"subtitle": "Selecciona tu idioma para continuar",
|
||||
"continue": "Continuar",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -370,5 +371,184 @@
|
||||
"move_title": "Mover {{count}} elemento(s)",
|
||||
"add_favorites": "Añadir a favoritos",
|
||||
"move_copy": "Mover o copiar"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "Panel de Administración",
|
||||
"back_to_app": "Volver a OxiCloud",
|
||||
"loading": "Cargando…",
|
||||
"access_denied": "Acceso Denegado",
|
||||
"access_denied_desc": "Se requieren privilegios de administrador para acceder a este panel.",
|
||||
"sign_in": "Iniciar sesión",
|
||||
"tab_dashboard": "Panel",
|
||||
"tab_users": "Usuarios",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "Usuarios Totales",
|
||||
"active_users": "Usuarios Activos",
|
||||
"admins": "Administradores",
|
||||
"version": "Versión",
|
||||
"storage_overview": "Resumen de Almacenamiento",
|
||||
"used": "Usado",
|
||||
"total_quota": "Cuota Total",
|
||||
"usage_pct": "Uso %",
|
||||
"users_over_80": "Usuarios >80% cuota",
|
||||
"users_over_quota": "Usuarios sobre cuota",
|
||||
"system": "Sistema",
|
||||
"auth_label": "Auth",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "Cuotas",
|
||||
"enabled": "Habilitado",
|
||||
"disabled": "Deshabilitado",
|
||||
"active": "Activo",
|
||||
"off": "Inactivo",
|
||||
"allow_registration": "Permitir registro público",
|
||||
"registration_warning": "El registro público está deshabilitado. Solo los administradores pueden crear nuevos usuarios.",
|
||||
"user_management": "Gestión de Usuarios",
|
||||
"create_user": "Crear Usuario",
|
||||
"col_user": "Usuario",
|
||||
"col_role": "Rol",
|
||||
"col_auth": "Auth",
|
||||
"col_status": "Estado",
|
||||
"col_storage": "Almacenamiento",
|
||||
"col_last_login": "Último Acceso",
|
||||
"col_actions": "Acciones",
|
||||
"loading_users": "Cargando usuarios…",
|
||||
"failed_load_users": "Error al cargar usuarios",
|
||||
"no_users_found": "No se encontraron usuarios",
|
||||
"showing_users": "Mostrando {{from}}-{{to}} de {{total}}",
|
||||
"prev": "Anterior",
|
||||
"next": "Siguiente",
|
||||
"inactive": "Inactivo",
|
||||
"you_badge": "(tú)",
|
||||
"local": "Local",
|
||||
"never": "Nunca",
|
||||
"just_now": "Ahora mismo",
|
||||
"minutes_ago": "hace {{n}}m",
|
||||
"hours_ago": "hace {{n}}h",
|
||||
"days_ago": "hace {{n}}d",
|
||||
"edit_quota_title": "Editar cuota",
|
||||
"reset_password_title": "Restablecer contraseña",
|
||||
"toggle_role_title": "Cambiar rol",
|
||||
"deactivate_title": "Desactivar",
|
||||
"activate_title": "Activar",
|
||||
"delete_title": "Eliminar",
|
||||
"sso_title": "Inicio de Sesión Único (OIDC / SSO)",
|
||||
"enable_sso": "Habilitar autenticación SSO",
|
||||
"provider_name": "Nombre del Proveedor",
|
||||
"issuer_url": "URL del Emisor",
|
||||
"issuer_url_hint": "URL del emisor OpenID Connect de tu proveedor de identidad",
|
||||
"auto_discover": "Auto-descubrir",
|
||||
"discovering": "Descubriendo…",
|
||||
"client_id": "Client ID",
|
||||
"client_secret": "Client Secret",
|
||||
"client_secret_placeholder": "Dejar vacío para mantener el valor actual",
|
||||
"secret_configured": "Ya hay un client secret configurado",
|
||||
"callback_url": "URL de Callback",
|
||||
"callback_url_hint": "(registrar en tu IdP)",
|
||||
"advanced_settings": "Configuración Avanzada",
|
||||
"scopes": "Scopes",
|
||||
"auto_provision": "Auto-provisionar usuarios en el primer inicio de sesión",
|
||||
"admin_groups": "Grupos de Admin",
|
||||
"admin_groups_hint": "Nombres de grupos OIDC separados por comas que mapean al rol de admin",
|
||||
"disable_password": "Desactivar inicio de sesión con contraseña (solo OIDC)",
|
||||
"password_warning": "¡Esto impedirá TODOS los inicios de sesión con contraseña!",
|
||||
"test_btn": "Probar",
|
||||
"save_btn": "Guardar",
|
||||
"saving": "Guardando…",
|
||||
"settings_saved": "Configuración guardada — OIDC ahora está {{status}}",
|
||||
"quota_modal_title": "Actualizar Cuota de Almacenamiento",
|
||||
"quota_user_label": "Usuario:",
|
||||
"new_quota": "Nueva Cuota",
|
||||
"quota_unlimited_hint": "Establecer 0 para ilimitado",
|
||||
"cancel": "Cancelar",
|
||||
"create_user_title": "Crear Nuevo Usuario",
|
||||
"username_label": "Nombre de usuario",
|
||||
"username_placeholder": "juanperez",
|
||||
"username_hint": "3–32 caracteres",
|
||||
"password_label": "Contraseña",
|
||||
"password_placeholder": "Mín 8 caracteres",
|
||||
"email_label": "Correo",
|
||||
"email_optional": "(opcional)",
|
||||
"email_placeholder": "usuario@ejemplo.com (auto-generado si vacío)",
|
||||
"role_label": "Rol",
|
||||
"role_user": "Usuario",
|
||||
"role_admin": "Admin",
|
||||
"quota_label": "Cuota",
|
||||
"creating": "Creando…",
|
||||
"reset_pw_title": "Restablecer Contraseña",
|
||||
"new_password_label": "Nueva Contraseña",
|
||||
"resetting": "Restableciendo…",
|
||||
"reset_btn": "Restablecer",
|
||||
"confirm_role_change": "¿Cambiar rol a {{role}}?",
|
||||
"confirm_deactivate": "¿Estás seguro de que quieres desactivar este usuario?",
|
||||
"confirm_activate": "¿Estás seguro de que quieres activar este usuario?",
|
||||
"confirm_delete_user": "¿ELIMINAR usuario \"{{name}}\"? ¡Esto no se puede deshacer!",
|
||||
"confirm_action": "Confirmar Acción",
|
||||
"confirm_yes": "Confirmar",
|
||||
"confirm_no": "Cancelar",
|
||||
"error_username_short": "El nombre de usuario debe tener al menos 3 caracteres",
|
||||
"error_password_short": "La contraseña debe tener al menos 8 caracteres",
|
||||
"error_generic": "Error",
|
||||
"error_network": "Error de red: {{message}}",
|
||||
"error_create_user": "Error al crear usuario"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "Perfil",
|
||||
"back_to_app": "Volver a OxiCloud",
|
||||
"loading": "Cargando…",
|
||||
"not_authenticated": "No Autenticado",
|
||||
"not_authenticated_desc": "Inicia sesión para ver tu perfil.",
|
||||
"sign_in": "Iniciar sesión",
|
||||
"role_admin": "Administrador",
|
||||
"role_user": "Usuario",
|
||||
"account_details": "Detalles de la Cuenta",
|
||||
"username": "Nombre de usuario",
|
||||
"email": "Correo electrónico",
|
||||
"role": "Rol",
|
||||
"last_login": "Último acceso",
|
||||
"storage": "Almacenamiento",
|
||||
"used": "Usado",
|
||||
"quota": "Cuota",
|
||||
"usage": "Uso",
|
||||
"unlimited": "Ilimitado",
|
||||
"app_passwords": "Contraseñas de Aplicación",
|
||||
"app_pw_desc": "Genera contraseñas para clientes WebDAV, CalDAV y CardDAV. Cada contraseña se muestra solo una vez.",
|
||||
"app_pw_label_placeholder": "Etiqueta (ej. Thunderbird, macOS)",
|
||||
"generate": "Generar",
|
||||
"generating": "Generando…",
|
||||
"new_password_for": "Nueva contraseña para",
|
||||
"copy_warning": "Copia esta contraseña ahora. No podrás verla de nuevo.",
|
||||
"copy_to_clipboard": "Copiar al portapapeles",
|
||||
"col_label": "Etiqueta",
|
||||
"col_created": "Creado",
|
||||
"col_last_used": "Último uso",
|
||||
"col_status": "Estado",
|
||||
"active": "Activa",
|
||||
"revoked": "Revocada",
|
||||
"revoke_title": "Revocar",
|
||||
"no_app_passwords": "Aún no hay contraseñas de aplicación.",
|
||||
"client_sessions": "Sesiones de cliente",
|
||||
"client_sessions_desc": "Generadas automáticamente al conectar un cliente compatible con Nextcloud.",
|
||||
"col_client": "Cliente",
|
||||
"never": "Nunca",
|
||||
"just_now": "Ahora mismo",
|
||||
"minutes_ago": "hace {{n}} min",
|
||||
"hours_ago": "hace {{n}}h",
|
||||
"days_ago": "hace {{n}} días",
|
||||
"change_password": "Cambiar Contraseña",
|
||||
"current_password": "Contraseña Actual",
|
||||
"new_password": "Nueva Contraseña",
|
||||
"min_8_chars": "Al menos 8 caracteres",
|
||||
"confirm_password": "Confirmar Nueva Contraseña",
|
||||
"update_password": "Actualizar Contraseña",
|
||||
"updating": "Actualizando…",
|
||||
"password_updated": "Contraseña actualizada correctamente",
|
||||
"passwords_no_match": "Las contraseñas no coinciden",
|
||||
"password_too_short": "La contraseña debe tener al menos 8 caracteres",
|
||||
"password_change_failed": "Error al cambiar la contraseña",
|
||||
"error_network": "Error de red: {{message}}",
|
||||
"error_label_required": "Introduce una etiqueta",
|
||||
"error_create_pw": "Error al crear contraseña de aplicación",
|
||||
"confirm_revoke": "¿Revocar contraseña \"{{label}}\"? Los clientes que la usen dejarán de funcionar.",
|
||||
"error_revoke": "Error al revocar contraseña"
|
||||
}
|
||||
}
|
||||
+183
-2
@@ -180,6 +180,8 @@
|
||||
"size": "اندازه",
|
||||
"modified": "تاریخ تغییر",
|
||||
"no_files": "هنوز هیچ پروندهای در این پوشه وجود ندارد",
|
||||
"empty_hint": "برای شروع، فایلها را آپلود کنید یا پوشه بسازید",
|
||||
"loading": "در حال بارگذاری فایلها…",
|
||||
"view_grid": "نمای شبکهای",
|
||||
"view_list": "نمای فهرستی",
|
||||
"file_types": {
|
||||
@@ -303,8 +305,8 @@
|
||||
"zoom_reset": "بازنشانی بزرگنمایی"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "خوش آمدید به OxiCloud",
|
||||
"subtitle": "لطفا زبان خود را انتخاب کنید",
|
||||
"title": "!خوش آمدید",
|
||||
"subtitle": "زبان خود را برای ادامه انتخاب کنید",
|
||||
"continue": "ادامه",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -340,5 +342,184 @@
|
||||
"move_title": "انتقال {{count}} مورد",
|
||||
"add_favorites": "افزودن به موارد علاقهمند",
|
||||
"move_copy": "انتقال یا کپی"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "پنل مدیریت",
|
||||
"back_to_app": "بازگشت به OxiCloud",
|
||||
"loading": "در حال بارگذاری…",
|
||||
"access_denied": "دسترسی ممنوع",
|
||||
"access_denied_desc": "امتیازات مدیر لازم است.",
|
||||
"sign_in": "ورود",
|
||||
"tab_dashboard": "داشبورد",
|
||||
"tab_users": "کاربران",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "کل کاربران",
|
||||
"active_users": "کاربران فعال",
|
||||
"admins": "مدیران",
|
||||
"version": "نسخه",
|
||||
"storage_overview": "نمای کلی فضا",
|
||||
"used": "استفاده شده",
|
||||
"total_quota": "سهمیه کل",
|
||||
"usage_pct": "درصد استفاده",
|
||||
"users_over_80": "کاربران بالای ۸۰٪",
|
||||
"users_over_quota": "کاربران بالای سهمیه",
|
||||
"system": "سیستم",
|
||||
"auth_label": "احراز هویت",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "سهمیهها",
|
||||
"enabled": "فعال",
|
||||
"disabled": "غیرفعال",
|
||||
"active": "فعال",
|
||||
"off": "خاموش",
|
||||
"allow_registration": "اجازه ثبتنام عمومی",
|
||||
"registration_warning": "ثبتنام عمومی غیرفعال است. فقط مدیران میتوانند کاربر جدید بسازند.",
|
||||
"user_management": "مدیریت کاربران",
|
||||
"create_user": "ایجاد کاربر",
|
||||
"col_user": "کاربر",
|
||||
"col_role": "نقش",
|
||||
"col_auth": "احراز هویت",
|
||||
"col_status": "وضعیت",
|
||||
"col_storage": "فضا",
|
||||
"col_last_login": "آخرین ورود",
|
||||
"col_actions": "عملیات",
|
||||
"loading_users": "در حال بارگذاری…",
|
||||
"failed_load_users": "خطا در بارگذاری",
|
||||
"no_users_found": "کاربری یافت نشد",
|
||||
"showing_users": "نمایش {{from}}-{{to}} از {{total}}",
|
||||
"prev": "قبلی",
|
||||
"next": "بعدی",
|
||||
"inactive": "غیرفعال",
|
||||
"you_badge": "(شما)",
|
||||
"local": "محلی",
|
||||
"never": "هرگز",
|
||||
"just_now": "همین الان",
|
||||
"minutes_ago": "{{n}} دقیقه پیش",
|
||||
"hours_ago": "{{n}} ساعت پیش",
|
||||
"days_ago": "{{n}} روز پیش",
|
||||
"edit_quota_title": "ویرایش سهمیه",
|
||||
"reset_password_title": "بازنشانی رمز",
|
||||
"toggle_role_title": "تغییر نقش",
|
||||
"deactivate_title": "غیرفعال کردن",
|
||||
"activate_title": "فعال کردن",
|
||||
"delete_title": "حذف",
|
||||
"sso_title": "ورود یکپارچه (OIDC / SSO)",
|
||||
"enable_sso": "فعالسازی SSO",
|
||||
"provider_name": "نام ارائهدهنده",
|
||||
"issuer_url": "آدرس صادرکننده",
|
||||
"issuer_url_hint": "آدرس صادرکننده OpenID Connect",
|
||||
"auto_discover": "کشف خودکار",
|
||||
"discovering": "در حال کشف…",
|
||||
"client_id": "شناسه مشتری",
|
||||
"client_secret": "رمز مشتری",
|
||||
"client_secret_placeholder": "خالی بگذارید تا مقدار فعلی حفظ شود",
|
||||
"secret_configured": "رمز مشتری قبلاً پیکربندی شده",
|
||||
"callback_url": "آدرس بازگشت",
|
||||
"callback_url_hint": "(در IdP ثبت کنید)",
|
||||
"advanced_settings": "تنظیمات پیشرفته",
|
||||
"scopes": "محدودهها",
|
||||
"auto_provision": "تامین خودکار کاربران",
|
||||
"admin_groups": "گروههای مدیر",
|
||||
"admin_groups_hint": "نام گروههای OIDC جدا شده با کاما",
|
||||
"disable_password": "غیرفعالسازی ورود با رمز (فقط OIDC)",
|
||||
"password_warning": "تمام ورودهای رمزی متوقف میشود!",
|
||||
"test_btn": "آزمایش",
|
||||
"save_btn": "ذخیره",
|
||||
"saving": "در حال ذخیره…",
|
||||
"settings_saved": "تنظیمات ذخیره شد — OIDC اکنون {{status}}",
|
||||
"quota_modal_title": "بهروزرسانی سهمیه",
|
||||
"quota_user_label": "کاربر:",
|
||||
"new_quota": "سهمیه جدید",
|
||||
"quota_unlimited_hint": "۰ برای نامحدود",
|
||||
"cancel": "انصراف",
|
||||
"create_user_title": "ایجاد کاربر جدید",
|
||||
"username_label": "نام کاربری",
|
||||
"username_placeholder": "نامکاربری",
|
||||
"username_hint": "۳ تا ۳۲ کاراکتر",
|
||||
"password_label": "رمز عبور",
|
||||
"password_placeholder": "حداقل ۸ کاراکتر",
|
||||
"email_label": "ایمیل",
|
||||
"email_optional": "(اختیاری)",
|
||||
"email_placeholder": "user@example.com (خودکار اگر خالی)",
|
||||
"role_label": "نقش",
|
||||
"role_user": "کاربر",
|
||||
"role_admin": "مدیر",
|
||||
"quota_label": "سهمیه",
|
||||
"creating": "در حال ایجاد…",
|
||||
"reset_pw_title": "بازنشانی رمز عبور",
|
||||
"new_password_label": "رمز عبور جدید",
|
||||
"resetting": "در حال بازنشانی…",
|
||||
"reset_btn": "بازنشانی",
|
||||
"confirm_role_change": "نقش به {{role}} تغییر یابد؟",
|
||||
"confirm_deactivate": "آیا از غیرفعالسازی این کاربر مطمئنید؟",
|
||||
"confirm_activate": "آیا از فعالسازی این کاربر مطمئنید؟",
|
||||
"confirm_delete_user": "کاربر «{{name}}» حذف شود؟ قابل بازگشت نیست!",
|
||||
"confirm_action": "تأیید عملیات",
|
||||
"confirm_yes": "تأیید",
|
||||
"confirm_no": "انصراف",
|
||||
"error_username_short": "نام کاربری حداقل ۳ کاراکتر",
|
||||
"error_password_short": "رمز عبور حداقل ۸ کاراکتر",
|
||||
"error_generic": "خطا",
|
||||
"error_network": "خطای شبکه: {{message}}",
|
||||
"error_create_user": "خطا در ایجاد کاربر"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "پروفایل",
|
||||
"back_to_app": "بازگشت به OxiCloud",
|
||||
"loading": "در حال بارگذاری…",
|
||||
"not_authenticated": "احراز هویت نشده",
|
||||
"not_authenticated_desc": "برای مشاهده پروفایل وارد شوید.",
|
||||
"sign_in": "ورود",
|
||||
"role_admin": "مدیر",
|
||||
"role_user": "کاربر",
|
||||
"account_details": "جزئیات حساب",
|
||||
"username": "نام کاربری",
|
||||
"email": "ایمیل",
|
||||
"role": "نقش",
|
||||
"last_login": "آخرین ورود",
|
||||
"storage": "فضای ذخیرهسازی",
|
||||
"used": "استفاده شده",
|
||||
"quota": "سهمیه",
|
||||
"usage": "مصرف",
|
||||
"unlimited": "نامحدود",
|
||||
"app_passwords": "رمزهای برنامه",
|
||||
"app_pw_desc": "رمزهایی برای کلاینتهای WebDAV، CalDAV و CardDAV ایجاد کنید. هر رمز فقط یک بار نمایش داده میشود.",
|
||||
"app_pw_label_placeholder": "برچسب (مثلاً Thunderbird، macOS)",
|
||||
"generate": "ایجاد",
|
||||
"generating": "در حال ایجاد…",
|
||||
"new_password_for": "رمز جدید برای",
|
||||
"copy_warning": "این رمز را اکنون کپی کنید. دوباره قابل مشاهده نیست.",
|
||||
"copy_to_clipboard": "کپی به کلیپبورد",
|
||||
"col_label": "برچسب",
|
||||
"col_created": "ایجاد شده",
|
||||
"col_last_used": "آخرین استفاده",
|
||||
"col_status": "وضعیت",
|
||||
"active": "فعال",
|
||||
"revoked": "ابطال شده",
|
||||
"revoke_title": "ابطال",
|
||||
"no_app_passwords": "هنوز رمز برنامهای وجود ندارد.",
|
||||
"client_sessions": "نشستهای کلاینت",
|
||||
"client_sessions_desc": "هنگام اتصال کلاینت سازگار با Nextcloud به صورت خودکار ایجاد میشود.",
|
||||
"col_client": "کلاینت",
|
||||
"never": "هرگز",
|
||||
"just_now": "همین الان",
|
||||
"minutes_ago": "{{n}} دقیقه پیش",
|
||||
"hours_ago": "{{n}} ساعت پیش",
|
||||
"days_ago": "{{n}} روز پیش",
|
||||
"change_password": "تغییر رمز عبور",
|
||||
"current_password": "رمز فعلی",
|
||||
"new_password": "رمز جدید",
|
||||
"min_8_chars": "حداقل ۸ کاراکتر",
|
||||
"confirm_password": "تأیید رمز جدید",
|
||||
"update_password": "بهروزرسانی رمز",
|
||||
"updating": "در حال بهروزرسانی…",
|
||||
"password_updated": "رمز عبور با موفقیت بهروز شد",
|
||||
"passwords_no_match": "رمزها مطابقت ندارند",
|
||||
"password_too_short": "رمز باید حداقل ۸ کاراکتر باشد",
|
||||
"password_change_failed": "تغییر رمز ناموفق بود",
|
||||
"error_network": "خطای شبکه: {{message}}",
|
||||
"error_label_required": "لطفاً برچسب وارد کنید",
|
||||
"error_create_pw": "ایجاد رمز ناموفق بود",
|
||||
"confirm_revoke": "رمز «{{label}}» ابطال شود؟ کلاینتها از کار میافتند.",
|
||||
"error_revoke": "ابطال ناموفق بود"
|
||||
}
|
||||
}
|
||||
+182
-2
@@ -182,6 +182,7 @@
|
||||
"size": "Taille",
|
||||
"modified": "Modifié",
|
||||
"no_files": "Aucun fichier dans ce dossier",
|
||||
"empty_hint": "Téléversez des fichiers ou créez des dossiers pour commencer",
|
||||
"loading": "Chargement des fichiers…",
|
||||
"view_grid": "Vue en grille",
|
||||
"view_list": "Vue en liste",
|
||||
@@ -317,8 +318,8 @@
|
||||
"zoom_reset": "Réinitialiser le zoom"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "Bienvenue sur OxiCloud",
|
||||
"subtitle": "Veuillez sélectionner votre langue",
|
||||
"title": "Bienvenue !",
|
||||
"subtitle": "Sélectionnez votre langue pour continuer",
|
||||
"continue": "Continuer",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -365,5 +366,184 @@
|
||||
"move_title": "Déplacer {{count}} élément(s)",
|
||||
"add_favorites": "Ajouter aux favoris",
|
||||
"move_copy": "Déplacer ou copier"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "Panneau d'administration",
|
||||
"back_to_app": "Retour à OxiCloud",
|
||||
"loading": "Chargement…",
|
||||
"access_denied": "Accès refusé",
|
||||
"access_denied_desc": "Privilèges d'administrateur requis.",
|
||||
"sign_in": "Se connecter",
|
||||
"tab_dashboard": "Tableau de bord",
|
||||
"tab_users": "Utilisateurs",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "Utilisateurs totaux",
|
||||
"active_users": "Utilisateurs actifs",
|
||||
"admins": "Admins",
|
||||
"version": "Version",
|
||||
"storage_overview": "Aperçu du stockage",
|
||||
"used": "Utilisé",
|
||||
"total_quota": "Quota total",
|
||||
"usage_pct": "Utilisation %",
|
||||
"users_over_80": "Utilisateurs >80% quota",
|
||||
"users_over_quota": "Utilisateurs dépassant le quota",
|
||||
"system": "Système",
|
||||
"auth_label": "Auth",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "Quotas",
|
||||
"enabled": "Activé",
|
||||
"disabled": "Désactivé",
|
||||
"active": "Actif",
|
||||
"off": "Inactif",
|
||||
"allow_registration": "Autoriser l'inscription publique",
|
||||
"registration_warning": "L'inscription publique est désactivée. Seuls les admins peuvent créer des utilisateurs.",
|
||||
"user_management": "Gestion des utilisateurs",
|
||||
"create_user": "Créer un utilisateur",
|
||||
"col_user": "Utilisateur",
|
||||
"col_role": "Rôle",
|
||||
"col_auth": "Auth",
|
||||
"col_status": "Statut",
|
||||
"col_storage": "Stockage",
|
||||
"col_last_login": "Dernière connexion",
|
||||
"col_actions": "Actions",
|
||||
"loading_users": "Chargement des utilisateurs…",
|
||||
"failed_load_users": "Échec du chargement",
|
||||
"no_users_found": "Aucun utilisateur trouvé",
|
||||
"showing_users": "Affichage {{from}}-{{to}} sur {{total}}",
|
||||
"prev": "Précédent",
|
||||
"next": "Suivant",
|
||||
"inactive": "Inactif",
|
||||
"you_badge": "(vous)",
|
||||
"local": "Local",
|
||||
"never": "Jamais",
|
||||
"just_now": "À l'instant",
|
||||
"minutes_ago": "il y a {{n}}min",
|
||||
"hours_ago": "il y a {{n}}h",
|
||||
"days_ago": "il y a {{n}}j",
|
||||
"edit_quota_title": "Modifier le quota",
|
||||
"reset_password_title": "Réinitialiser le mot de passe",
|
||||
"toggle_role_title": "Changer de rôle",
|
||||
"deactivate_title": "Désactiver",
|
||||
"activate_title": "Activer",
|
||||
"delete_title": "Supprimer",
|
||||
"sso_title": "Authentification unique (OIDC / SSO)",
|
||||
"enable_sso": "Activer l'authentification SSO",
|
||||
"provider_name": "Nom du fournisseur",
|
||||
"issuer_url": "URL de l'émetteur",
|
||||
"issuer_url_hint": "URL de l'émetteur OpenID Connect",
|
||||
"auto_discover": "Auto-découverte",
|
||||
"discovering": "Découverte…",
|
||||
"client_id": "Client ID",
|
||||
"client_secret": "Client Secret",
|
||||
"client_secret_placeholder": "Laisser vide pour conserver la valeur",
|
||||
"secret_configured": "Un client secret est déjà configuré",
|
||||
"callback_url": "URL de rappel",
|
||||
"callback_url_hint": "(enregistrer dans votre IdP)",
|
||||
"advanced_settings": "Paramètres avancés",
|
||||
"scopes": "Scopes",
|
||||
"auto_provision": "Provisionner automatiquement les utilisateurs",
|
||||
"admin_groups": "Groupes admin",
|
||||
"admin_groups_hint": "Noms de groupes OIDC séparés par des virgules",
|
||||
"disable_password": "Désactiver la connexion par mot de passe (OIDC uniquement)",
|
||||
"password_warning": "Cela empêchera TOUTES les connexions par mot de passe !",
|
||||
"test_btn": "Tester",
|
||||
"save_btn": "Enregistrer",
|
||||
"saving": "Enregistrement…",
|
||||
"settings_saved": "Paramètres enregistrés — OIDC est maintenant {{status}}",
|
||||
"quota_modal_title": "Mettre à jour le quota",
|
||||
"quota_user_label": "Utilisateur :",
|
||||
"new_quota": "Nouveau quota",
|
||||
"quota_unlimited_hint": "0 pour illimité",
|
||||
"cancel": "Annuler",
|
||||
"create_user_title": "Créer un nouvel utilisateur",
|
||||
"username_label": "Nom d'utilisateur",
|
||||
"username_placeholder": "jeandupont",
|
||||
"username_hint": "3–32 caractères",
|
||||
"password_label": "Mot de passe",
|
||||
"password_placeholder": "Min 8 caractères",
|
||||
"email_label": "E-mail",
|
||||
"email_optional": "(facultatif)",
|
||||
"email_placeholder": "utilisateur@exemple.com (auto-généré si vide)",
|
||||
"role_label": "Rôle",
|
||||
"role_user": "Utilisateur",
|
||||
"role_admin": "Admin",
|
||||
"quota_label": "Quota",
|
||||
"creating": "Création…",
|
||||
"reset_pw_title": "Réinitialiser le mot de passe",
|
||||
"new_password_label": "Nouveau mot de passe",
|
||||
"resetting": "Réinitialisation…",
|
||||
"reset_btn": "Réinitialiser",
|
||||
"confirm_role_change": "Changer le rôle en {{role}} ?",
|
||||
"confirm_deactivate": "Voulez-vous vraiment désactiver cet utilisateur ?",
|
||||
"confirm_activate": "Voulez-vous vraiment activer cet utilisateur ?",
|
||||
"confirm_delete_user": "SUPPRIMER l'utilisateur \"{{name}}\" ? Irréversible !",
|
||||
"confirm_action": "Confirmer l'action",
|
||||
"confirm_yes": "Confirmer",
|
||||
"confirm_no": "Annuler",
|
||||
"error_username_short": "Le nom d'utilisateur doit contenir au moins 3 caractères",
|
||||
"error_password_short": "Le mot de passe doit contenir au moins 8 caractères",
|
||||
"error_generic": "Échec",
|
||||
"error_network": "Erreur réseau : {{message}}",
|
||||
"error_create_user": "Impossible de créer l'utilisateur"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "Profil",
|
||||
"back_to_app": "Retour à OxiCloud",
|
||||
"loading": "Chargement…",
|
||||
"not_authenticated": "Non authentifié",
|
||||
"not_authenticated_desc": "Connectez-vous pour voir votre profil.",
|
||||
"sign_in": "Se connecter",
|
||||
"role_admin": "Administrateur",
|
||||
"role_user": "Utilisateur",
|
||||
"account_details": "Détails du compte",
|
||||
"username": "Nom d'utilisateur",
|
||||
"email": "E-mail",
|
||||
"role": "Rôle",
|
||||
"last_login": "Dernière connexion",
|
||||
"storage": "Stockage",
|
||||
"used": "Utilisé",
|
||||
"quota": "Quota",
|
||||
"usage": "Utilisation",
|
||||
"unlimited": "Illimité",
|
||||
"app_passwords": "Mots de passe d'application",
|
||||
"app_pw_desc": "Générez des mots de passe pour les clients WebDAV, CalDAV et CardDAV. Chaque mot de passe n'est affiché qu'une seule fois.",
|
||||
"app_pw_label_placeholder": "Libellé (ex. Thunderbird, macOS)",
|
||||
"generate": "Générer",
|
||||
"generating": "Génération…",
|
||||
"new_password_for": "Nouveau mot de passe pour",
|
||||
"copy_warning": "Copiez ce mot de passe maintenant. Vous ne pourrez plus le revoir.",
|
||||
"copy_to_clipboard": "Copier dans le presse-papiers",
|
||||
"col_label": "Libellé",
|
||||
"col_created": "Créé",
|
||||
"col_last_used": "Dernière utilisation",
|
||||
"col_status": "Statut",
|
||||
"active": "Actif",
|
||||
"revoked": "Révoqué",
|
||||
"revoke_title": "Révoquer",
|
||||
"no_app_passwords": "Aucun mot de passe d'application.",
|
||||
"client_sessions": "Sessions client",
|
||||
"client_sessions_desc": "Générées automatiquement lors de la connexion d'un client compatible Nextcloud.",
|
||||
"col_client": "Client",
|
||||
"never": "Jamais",
|
||||
"just_now": "À l'instant",
|
||||
"minutes_ago": "il y a {{n}} min",
|
||||
"hours_ago": "il y a {{n}}h",
|
||||
"days_ago": "il y a {{n}} jours",
|
||||
"change_password": "Changer le mot de passe",
|
||||
"current_password": "Mot de passe actuel",
|
||||
"new_password": "Nouveau mot de passe",
|
||||
"min_8_chars": "Au moins 8 caractères",
|
||||
"confirm_password": "Confirmer le nouveau mot de passe",
|
||||
"update_password": "Mettre à jour le mot de passe",
|
||||
"updating": "Mise à jour…",
|
||||
"password_updated": "Mot de passe mis à jour avec succès",
|
||||
"passwords_no_match": "Les mots de passe ne correspondent pas",
|
||||
"password_too_short": "Le mot de passe doit contenir au moins 8 caractères",
|
||||
"password_change_failed": "Échec du changement de mot de passe",
|
||||
"error_network": "Erreur réseau : {{message}}",
|
||||
"error_label_required": "Veuillez entrer un libellé",
|
||||
"error_create_pw": "Impossible de créer le mot de passe",
|
||||
"confirm_revoke": "Révoquer le mot de passe \"{{label}}\" ? Les clients l'utilisant ne fonctionneront plus.",
|
||||
"error_revoke": "Échec de la révocation"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
{
|
||||
"app": {
|
||||
"title": "OxiCloud",
|
||||
"description": "न्यूनतम क्लाउड स्टोरेज सिस्टम"
|
||||
},
|
||||
"nav": {
|
||||
"files": "फ़ाइलें",
|
||||
"shared": "साझा",
|
||||
"recent": "हाल ही में",
|
||||
"favorites": "पसंदीदा",
|
||||
"photos": "फ़ोटो",
|
||||
"trash": "रद्दी"
|
||||
},
|
||||
"photos": {
|
||||
"empty_state": "अभी कोई फ़ोटो नहीं",
|
||||
"empty_hint": "यहाँ देखने के लिए चित्र या वीडियो अपलोड करें",
|
||||
"items_selected": "चयनित",
|
||||
"view_daily": "दिन",
|
||||
"view_monthly": "महीना",
|
||||
"view_yearly": "वर्ष"
|
||||
},
|
||||
"actions": {
|
||||
"search": "फ़ाइलें खोजें...",
|
||||
"new_folder": "नया फ़ोल्डर",
|
||||
"upload": "अपलोड",
|
||||
"upload_files": "फ़ाइलें अपलोड करें",
|
||||
"upload_folder": "फ़ोल्डर अपलोड करें",
|
||||
"upload.uploading": "अपलोड हो रहा है...",
|
||||
"upload.complete": "{count} / {total} अपलोड हुईं",
|
||||
"rename": "नाम बदलें",
|
||||
"move": "यहाँ ले जाएँ...",
|
||||
"move_to": "यहाँ ले जाएँ",
|
||||
"delete": "हटाएँ",
|
||||
"download": "डाउनलोड",
|
||||
"view": "देखें",
|
||||
"cancel": "रद्द करें",
|
||||
"confirm": "पुष्टि करें",
|
||||
"share": "साझा करें",
|
||||
"favorite": "पसंदीदा में जोड़ें",
|
||||
"unfavorite": "पसंदीदा से हटाएँ",
|
||||
"copy": "कॉपी करें",
|
||||
"notify": "सूचित करें",
|
||||
"send": "भेजें",
|
||||
"clear_recent": "हाल ही का साफ़ करें",
|
||||
"logout": "लॉग आउट",
|
||||
"create": "बनाएँ",
|
||||
"search_btn": "खोजें",
|
||||
"close": "बंद करें",
|
||||
"delete_permanently": "स्थायी रूप से हटाएँ",
|
||||
"empty_trash": "रद्दी खाली करें"
|
||||
},
|
||||
"user_menu": {
|
||||
"appearance": "दिखावट",
|
||||
"about": "OxiCloud के बारे में",
|
||||
"about_description": "Rust और Clean Architecture से बना क्लाउड स्टोरेज प्लेटफ़ॉर्म। तेज़, सुरक्षित और निजी।",
|
||||
"admin_panel": "एडमिन पैनल",
|
||||
"profile": "मेरी प्रोफ़ाइल",
|
||||
"role_user": "उपयोगकर्ता"
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "शेयर लिंक",
|
||||
"linkLabel": "शेयर लिंक:",
|
||||
"copyLink": "कॉपी",
|
||||
"permissions": "अनुमतियाँ:",
|
||||
"permissionRead": "पढ़ें",
|
||||
"permissionWrite": "लिखें",
|
||||
"permissionReshare": "पुनः साझा करें",
|
||||
"password": "पासवर्ड सुरक्षा:",
|
||||
"generatePassword": "जनरेट करें",
|
||||
"expiration": "समाप्ति तिथि:",
|
||||
"update": "शेयर अपडेट करें",
|
||||
"remove": "शेयर हटाएँ",
|
||||
"notifyTitle": "सूचना भेजें",
|
||||
"notifyEmailLabel": "ईमेल पता:",
|
||||
"notifyMessageLabel": "संदेश (वैकल्पिक):",
|
||||
"notifySend": "सूचना भेजें",
|
||||
"shareWithOthers": "दूसरों के साथ साझा करें",
|
||||
"sharePublicly": "सार्वजनिक रूप से साझा करें",
|
||||
"shareSettings": "साझा सेटिंग्स",
|
||||
"shareCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ",
|
||||
"shareCreated": "शेयर लिंक सफलतापूर्वक बनाया गया",
|
||||
"shareUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं",
|
||||
"shareRemoved": "शेयर सफलतापूर्वक हटाया गया"
|
||||
},
|
||||
"share_dialogTitle": "शेयर लिंक",
|
||||
"share_linkLabel": "शेयर लिंक:",
|
||||
"share_copyLink": "कॉपी",
|
||||
"share_permissions": "अनुमतियाँ:",
|
||||
"share_permissionRead": "पढ़ें",
|
||||
"share_permissionWrite": "लिखें",
|
||||
"share_permissionReshare": "पुनः साझा करें",
|
||||
"share_password": "पासवर्ड सुरक्षा:",
|
||||
"share_generatePassword": "जनरेट करें",
|
||||
"share_expiration": "समाप्ति तिथि:",
|
||||
"share_update": "शेयर अपडेट करें",
|
||||
"share_remove": "शेयर हटाएँ",
|
||||
"share_notifyTitle": "सूचना भेजें",
|
||||
"share_notifyEmailLabel": "ईमेल पता:",
|
||||
"share_notifyMessageLabel": "संदेश (वैकल्पिक):",
|
||||
"share_notifySend": "सूचना भेजें",
|
||||
"shared": {
|
||||
"backToFiles": "फ़ाइलों पर वापस",
|
||||
"pageTitle": "साझा संसाधन",
|
||||
"pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें",
|
||||
"filterType": "प्रकार:",
|
||||
"filterAll": "सभी",
|
||||
"filterFiles": "फ़ाइलें",
|
||||
"filterFolders": "फ़ोल्डर",
|
||||
"sortBy": "क्रमबद्ध:",
|
||||
"sortByName": "नाम",
|
||||
"sortByDate": "साझा तिथि",
|
||||
"sortByExpiration": "समाप्ति",
|
||||
"search": "खोजें",
|
||||
"colName": "नाम",
|
||||
"colType": "प्रकार",
|
||||
"colDateShared": "साझा तिथि",
|
||||
"colExpiration": "समाप्ति",
|
||||
"colPermissions": "अनुमतियाँ",
|
||||
"colPassword": "पासवर्ड",
|
||||
"colActions": "कार्य",
|
||||
"emptyStateTitle": "अभी कोई साझा संसाधन नहीं",
|
||||
"emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे",
|
||||
"goToFiles": "फ़ाइलों पर जाएँ",
|
||||
"typeFile": "फ़ाइल",
|
||||
"typeFolder": "फ़ोल्डर",
|
||||
"noExpiration": "कोई समाप्ति नहीं",
|
||||
"hasPassword": "हाँ",
|
||||
"noPassword": "नहीं",
|
||||
"editShare": "शेयर संपादित करें",
|
||||
"notifyShare": "किसी को सूचित करें",
|
||||
"copyLink": "लिंक कॉपी करें",
|
||||
"removeShare": "शेयर हटाएँ",
|
||||
"linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!",
|
||||
"linkCopyFailed": "लिंक कॉपी करने में विफल",
|
||||
"itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं",
|
||||
"itemRemoved": "शेयर सफलतापूर्वक हटाया गया",
|
||||
"invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें",
|
||||
"notificationSent": "सूचना सफलतापूर्वक भेजी गई",
|
||||
"notificationFailed": "सूचना भेजने में विफल",
|
||||
"shared_backToFiles": "फ़ाइलों पर वापस",
|
||||
"shared_pageTitle": "साझा संसाधन",
|
||||
"shared_pageDescription": "अपनी साझा फ़ाइलें और फ़ोल्डर प्रबंधित करें",
|
||||
"shared_filterType": "प्रकार:",
|
||||
"shared_filterAll": "सभी",
|
||||
"shared_filterFiles": "फ़ाइलें",
|
||||
"shared_filterFolders": "फ़ोल्डर",
|
||||
"shared_sortBy": "क्रमबद्ध:",
|
||||
"shared_sortByName": "नाम",
|
||||
"shared_sortByDate": "साझा तिथि",
|
||||
"shared_sortByExpiration": "समाप्ति",
|
||||
"shared_search": "खोजें",
|
||||
"shared_colName": "नाम",
|
||||
"shared_colType": "प्रकार",
|
||||
"shared_colDateShared": "साझा तिथि",
|
||||
"shared_colExpiration": "समाप्ति",
|
||||
"shared_colPermissions": "अनुमतियाँ",
|
||||
"shared_colPassword": "पासवर्ड",
|
||||
"shared_colActions": "कार्य",
|
||||
"shared_emptyStateTitle": "अभी कोई साझा संसाधन नहीं",
|
||||
"shared_emptyStateDesc": "जब आप फ़ाइलें या फ़ोल्डर साझा करेंगे, वे यहाँ दिखेंगे",
|
||||
"shared_goToFiles": "फ़ाइलों पर जाएँ",
|
||||
"shared_typeFile": "फ़ाइल",
|
||||
"shared_typeFolder": "फ़ोल्डर",
|
||||
"shared_noExpiration": "कोई समाप्ति नहीं",
|
||||
"shared_hasPassword": "हाँ",
|
||||
"shared_noPassword": "नहीं",
|
||||
"shared_editShare": "शेयर संपादित करें",
|
||||
"shared_notifyShare": "किसी को सूचित करें",
|
||||
"shared_copyLink": "लिंक कॉपी करें",
|
||||
"shared_removeShare": "शेयर हटाएँ",
|
||||
"shared_linkCopied": "लिंक क्लिपबोर्ड पर कॉपी हुआ!",
|
||||
"shared_linkCopyFailed": "लिंक कॉपी करने में विफल",
|
||||
"shared_itemUpdated": "शेयर सेटिंग्स सफलतापूर्वक अपडेट हुईं",
|
||||
"shared_itemRemoved": "शेयर सफलतापूर्वक हटाया गया",
|
||||
"shared_invalidEmail": "कृपया एक वैध ईमेल पता दर्ज करें",
|
||||
"shared_notificationSent": "सूचना सफलतापूर्वक भेजी गई",
|
||||
"shared_notificationFailed": "सूचना भेजने में विफल"
|
||||
},
|
||||
"files": {
|
||||
"name": "नाम",
|
||||
"type": "प्रकार",
|
||||
"size": "आकार",
|
||||
"modified": "संशोधित",
|
||||
"no_files": "इस फ़ोल्डर में कोई फ़ाइल नहीं",
|
||||
"empty_hint": "शुरू करने के लिए ह़ैलें अपलोड करें या होल्डर बनाएँ",
|
||||
"loading": "फ़ाइलें लोड हो रही हैं…",
|
||||
"view_grid": "ग्रिड दृश्य",
|
||||
"view_list": "सूची दृश्य",
|
||||
"file_types": {
|
||||
"document": "दस्तावेज़",
|
||||
"image": "चित्र",
|
||||
"video": "वीडियो",
|
||||
"audio": "ऑडियो",
|
||||
"pdf": "PDF",
|
||||
"text": "टेक्स्ट",
|
||||
"folder": "फ़ोल्डर",
|
||||
"spreadsheet": "स्प्रेडशीट",
|
||||
"presentation": "प्रेज़ेंटेशन",
|
||||
"archive": "संग्रह",
|
||||
"installer": "इंस्टॉलर",
|
||||
"code": "कोड"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
"rename_folder": "फ़ोल्डर का नाम बदलें",
|
||||
"rename_file": "फ़ाइल का नाम बदलें",
|
||||
"new_name": "नया नाम",
|
||||
"new_folder_title": "नया फ़ोल्डर",
|
||||
"folder_name": "फ़ोल्डर का नाम",
|
||||
"folder_placeholder": "मेरा फ़ोल्डर",
|
||||
"rename_title": "नाम बदलें",
|
||||
"move_file": "फ़ाइल ले जाएँ",
|
||||
"move_folder": "फ़ोल्डर ले जाएँ",
|
||||
"select_destination": "गंतव्य फ़ोल्डर चुनें:",
|
||||
"select_this_folder": "यह फ़ोल्डर चुनें",
|
||||
"go_to_parent": ".. (पैरेंट फ़ोल्डर)",
|
||||
"no_subfolders": "कोई सब-फ़ोल्डर नहीं",
|
||||
"root": "रूट",
|
||||
"delete_confirmation": "क्या आप वाकई हटाना चाहते हैं",
|
||||
"and_contents": "और इसकी सभी सामग्री",
|
||||
"no_undo": "यह कार्य पूर्ववत नहीं किया जा सकता",
|
||||
"confirm_title": "कार्य की पुष्टि करें",
|
||||
"confirm_delete": "रद्दी में भेजें",
|
||||
"confirm_delete_file": "क्या आप वाकई फ़ाइल \"{{name}}\" को रद्दी में भेजना चाहते हैं?",
|
||||
"confirm_delete_folder": "क्या आप वाकई फ़ोल्डर \"{{name}}\" और उसकी सभी सामग्री को रद्दी में भेजना चाहते हैं?",
|
||||
"confirm_permanent_delete": "स्थायी रूप से हटाएँ",
|
||||
"confirm_permanent_delete_msg": "क्या आप वाकई इस आइटम को स्थायी रूप से हटाना चाहते हैं? यह कार्य पूर्ववत नहीं किया जा सकता।",
|
||||
"confirm_empty_trash": "रद्दी खाली करें",
|
||||
"confirm_delete_share": "शेयर लिंक हटाएँ",
|
||||
"confirm_delete_share_msg": "क्या आप वाकई इस शेयर लिंक को हटाना चाहते हैं?",
|
||||
"share_file": "फ़ाइल साझा करें",
|
||||
"existing_shares": "मौजूदा शेयर",
|
||||
"share_options": "शेयर विकल्प",
|
||||
"password": "पासवर्ड",
|
||||
"expiration": "समाप्ति",
|
||||
"permissions": "अनुमतियाँ",
|
||||
"generated_link": "जनरेट किया गया लिंक",
|
||||
"notify": "सूचना भेजें",
|
||||
"recipient": "प्राप्तकर्ता",
|
||||
"message": "संदेश"
|
||||
},
|
||||
"dropzone": {
|
||||
"drag_files": "फ़ाइलें यहाँ खींचें या चुनने के लिए क्लिक करें",
|
||||
"drop_files": "अपलोड करने के लिए फ़ाइलें छोड़ें"
|
||||
},
|
||||
"permissions": {
|
||||
"read": "पढ़ें",
|
||||
"write": "लिखें",
|
||||
"reshare": "पुनः साझा करें"
|
||||
},
|
||||
"errors": {
|
||||
"file_not_found": "फ़ाइल नहीं मिली",
|
||||
"folder_not_found": "फ़ोल्डर नहीं मिला",
|
||||
"delete_error": "हटाने में त्रुटि",
|
||||
"upload_error": "फ़ाइल अपलोड करने में त्रुटि",
|
||||
"rename_error": "नाम बदलने में त्रुटि",
|
||||
"move_error": "ले जाने में त्रुटि",
|
||||
"empty_name": "नाम खाली नहीं हो सकता",
|
||||
"name_exists": "इस नाम की फ़ाइल या फ़ोल्डर पहले से मौजूद है",
|
||||
"generic_error": "एक त्रुटि हुई है"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"home": "होम"
|
||||
},
|
||||
"trash": {
|
||||
"empty_trash": "रद्दी खाली करें",
|
||||
"empty_state": "रद्दी खाली है",
|
||||
"original_location": "मूल स्थान",
|
||||
"deleted_date": "हटाने की तिथि",
|
||||
"actions": "कार्य",
|
||||
"restore": "पुनर्स्थापित करें",
|
||||
"delete_permanently": "स्थायी रूप से हटाएँ",
|
||||
"empty_confirm": "क्या आप वाकई रद्दी खाली करना चाहते हैं? यह सभी आइटम स्थायी रूप से हटा देगा।"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "साइन इन",
|
||||
"username": "उपयोगकर्ता नाम",
|
||||
"username_placeholder": "अपना उपयोगकर्ता नाम दर्ज करें",
|
||||
"password": "पासवर्ड",
|
||||
"password_placeholder": "अपना पासवर्ड दर्ज करें",
|
||||
"login_button": "साइन इन",
|
||||
"no_account": "खाता नहीं है?",
|
||||
"register": "साइन अप करें",
|
||||
"admin_setup": "पहली बार?",
|
||||
"setup": "एडमिन सेटअप करें",
|
||||
"register_title": "खाता बनाएँ",
|
||||
"email": "ईमेल",
|
||||
"email_placeholder": "अपना ईमेल दर्ज करें",
|
||||
"confirm_password": "पासवर्ड की पुष्टि करें",
|
||||
"confirm_password_placeholder": "अपना पासवर्ड पुष्टि करें",
|
||||
"register_button": "खाता बनाएँ",
|
||||
"have_account": "पहले से खाता है?",
|
||||
"login": "साइन इन",
|
||||
"setup_title": "प्रारंभिक सेटअप",
|
||||
"setup_step1": "एडमिन",
|
||||
"setup_step2": "सिस्टम",
|
||||
"setup_step3": "पूर्ण",
|
||||
"admin_username": "एडमिन उपयोगकर्ता नाम",
|
||||
"admin_email": "एडमिन ईमेल",
|
||||
"admin_password": "एडमिन पासवर्ड",
|
||||
"create_admin": "एडमिन बनाएँ",
|
||||
"back_to_login": "पहले से सेटअप है?",
|
||||
"admin_success": "एडमिन खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।",
|
||||
"account_success": "खाता सफलतापूर्वक बनाया गया! अब आप साइन इन कर सकते हैं।",
|
||||
"passwords_mismatch": "पासवर्ड मेल नहीं खाते",
|
||||
"admin_create_error": "एडमिन खाता बनाने में त्रुटि",
|
||||
"or": "या",
|
||||
"sso_login": "SSO से साइन इन करें",
|
||||
"sso_login_provider": "{{provider}} से साइन इन करें"
|
||||
},
|
||||
"storage": {
|
||||
"title": "स्टोरेज",
|
||||
"calculating": "गणना हो रही है...",
|
||||
"used": "{{percentage}}% उपयोग ({{used}} / {{total}})"
|
||||
},
|
||||
"viewer": {
|
||||
"unsupported_file": "इस फ़ाइल प्रकार का पूर्वावलोकन नहीं किया जा सकता।",
|
||||
"download_file": "फ़ाइल डाउनलोड करें",
|
||||
"zoom_in": "ज़ूम इन",
|
||||
"zoom_out": "ज़ूम आउट",
|
||||
"zoom_reset": "ज़ूम रीसेट"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "स्वागत है!",
|
||||
"subtitle": "जारी रखने के लिए अपनी भाषा चुनें",
|
||||
"continue": "आगे बढ़ें",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"zh": "中文",
|
||||
"fa": "فارسی",
|
||||
"fr": "Français",
|
||||
"de": "Deutsch",
|
||||
"pt": "Português",
|
||||
"hi": "हिन्दी"
|
||||
}
|
||||
},
|
||||
"favorites": {
|
||||
"empty_state": "अभी कोई पसंदीदा नहीं",
|
||||
"empty_hint": "पसंदीदा में जोड़ने के लिए फ़ाइलों या फ़ोल्डर को स्टार करें",
|
||||
"add": "पसंदीदा में जोड़ें",
|
||||
"remove": "पसंदीदा से हटाएँ",
|
||||
"added_title": "पसंदीदा में जोड़ा गया",
|
||||
"added_msg": "पसंदीदा में जोड़ा गया",
|
||||
"removed_title": "पसंदीदा से हटाया गया",
|
||||
"removed_msg": "पसंदीदा से हटाया गया"
|
||||
},
|
||||
"recent": {
|
||||
"title": "हाल ही में",
|
||||
"clear": "हाल ही का साफ़ करें",
|
||||
"accessed": "एक्सेस किया",
|
||||
"empty_state": "कोई हाल की फ़ाइलें नहीं",
|
||||
"empty_hint": "जो फ़ाइलें आप खोलेंगे वे यहाँ दिखेंगी"
|
||||
},
|
||||
"notifications": {
|
||||
"file_renamed": "फ़ाइल का नाम बदला गया",
|
||||
"file_renamed_to": "फ़ाइल का नाम \"{{name}}\" रखा गया",
|
||||
"folder_renamed": "फ़ोल्डर का नाम बदला गया",
|
||||
"folder_renamed_to": "फ़ोल्डर का नाम \"{{name}}\" रखा गया",
|
||||
"file_uploaded": "फ़ाइल अपलोड हुई",
|
||||
"file_deleted": "फ़ाइल रद्दी में भेजी गई",
|
||||
"folder_deleted": "फ़ोल्डर रद्दी में भेजा गया",
|
||||
"item_deleted_permanently": "आइटम स्थायी रूप से हटाया गया",
|
||||
"trash_emptied": "रद्दी सफलतापूर्वक खाली की गई",
|
||||
"title": "सूचनाएँ",
|
||||
"empty": "कोई सूचना नहीं"
|
||||
},
|
||||
"batch": {
|
||||
"one_selected": "1 आइटम चयनित",
|
||||
"n_selected": "{{count}} आइटम चयनित",
|
||||
"confirm_delete": "क्या आप वाकई {{count}} आइटम रद्दी में भेजना चाहते हैं?",
|
||||
"move_title": "{{count}} आइटम ले जाएँ",
|
||||
"add_favorites": "पसंदीदा में जोड़ें",
|
||||
"move_copy": "ले जाएँ या कॉपी करें"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "एडमिन पैनल",
|
||||
"back_to_app": "OxiCloud पर वापस",
|
||||
"loading": "लोड हो रहा है…",
|
||||
"access_denied": "पहुंच अस्वीकृत",
|
||||
"access_denied_desc": "व्यवस्थापक विशेषाधिकार आवश्यक।",
|
||||
"sign_in": "साइन इन",
|
||||
"tab_dashboard": "डैशबोर्ड",
|
||||
"tab_users": "उपयोगकर्ता",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "कुल उपयोगकर्ता",
|
||||
"active_users": "सक्रिय उपयोगकर्ता",
|
||||
"admins": "व्यवस्थापक",
|
||||
"version": "संस्करण",
|
||||
"storage_overview": "स्टोरेज अवलोकन",
|
||||
"used": "उपयोग किया",
|
||||
"total_quota": "कुल कोटा",
|
||||
"usage_pct": "उपयोग %",
|
||||
"users_over_80": ">80% कोटा वाले",
|
||||
"users_over_quota": "कोटा से अधिक",
|
||||
"system": "सिस्टम",
|
||||
"auth_label": "प्रमाणीकरण",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "कोटा",
|
||||
"enabled": "सक्षम",
|
||||
"disabled": "अक्षम",
|
||||
"active": "सक्रिय",
|
||||
"off": "बंद",
|
||||
"allow_registration": "सार्वजनिक पंजीकरण की अनुमति",
|
||||
"registration_warning": "सार्वजनिक पंजीकरण अक्षम है। केवल व्यवस्थापक उपयोगकर्ता बना सकते हैं।",
|
||||
"user_management": "उपयोगकर्ता प्रबंधन",
|
||||
"create_user": "उपयोगकर्ता बनाएं",
|
||||
"col_user": "उपयोगकर्ता",
|
||||
"col_role": "भूमिका",
|
||||
"col_auth": "प्रमाणीकरण",
|
||||
"col_status": "स्थिति",
|
||||
"col_storage": "स्टोरेज",
|
||||
"col_last_login": "अंतिम लॉगिन",
|
||||
"col_actions": "कार्रवाई",
|
||||
"loading_users": "उपयोगकर्ता लोड हो रहे हैं…",
|
||||
"failed_load_users": "लोड करने में विफल",
|
||||
"no_users_found": "कोई उपयोगकर्ता नहीं मिला",
|
||||
"showing_users": "{{from}}-{{to}} / {{total}} दिखा रहे हैं",
|
||||
"prev": "पिछला",
|
||||
"next": "अगला",
|
||||
"inactive": "निष्क्रिय",
|
||||
"you_badge": "(आप)",
|
||||
"local": "स्थानीय",
|
||||
"never": "कभी नहीं",
|
||||
"just_now": "अभी",
|
||||
"minutes_ago": "{{n}} मिनट पहले",
|
||||
"hours_ago": "{{n}} घंटे पहले",
|
||||
"days_ago": "{{n}} दिन पहले",
|
||||
"edit_quota_title": "कोटा संपादित करें",
|
||||
"reset_password_title": "पासवर्ड रीसेट",
|
||||
"toggle_role_title": "भूमिका बदलें",
|
||||
"deactivate_title": "निष्क्रिय करें",
|
||||
"activate_title": "सक्रिय करें",
|
||||
"delete_title": "हटाएं",
|
||||
"sso_title": "सिंगल साइन-ऑन (OIDC / SSO)",
|
||||
"enable_sso": "SSO सक्षम करें",
|
||||
"provider_name": "प्रदाता का नाम",
|
||||
"issuer_url": "जारीकर्ता URL",
|
||||
"issuer_url_hint": "OpenID Connect जारीकर्ता URL",
|
||||
"auto_discover": "स्वतः खोज",
|
||||
"discovering": "खोज रहे हैं…",
|
||||
"client_id": "क्लाइंट ID",
|
||||
"client_secret": "क्लाइंट सीक्रेट",
|
||||
"client_secret_placeholder": "वर्तमान मान बनाए रखने के लिए खाली छोड़ें",
|
||||
"secret_configured": "क्लाइंट सीक्रेट पहले से कॉन्फ़िगर है",
|
||||
"callback_url": "कॉलबैक URL",
|
||||
"callback_url_hint": "(अपने IdP में पंजीकृत करें)",
|
||||
"advanced_settings": "उन्नत सेटिंग्स",
|
||||
"scopes": "स्कोप",
|
||||
"auto_provision": "पहले लॉगिन पर स्वतः प्रावधान",
|
||||
"admin_groups": "व्यवस्थापक समूह",
|
||||
"admin_groups_hint": "अल्पविराम-पृथक OIDC समूह नाम",
|
||||
"disable_password": "पासवर्ड लॉगिन अक्षम (केवल OIDC)",
|
||||
"password_warning": "सभी पासवर्ड लॉगिन रुक जाएंगे!",
|
||||
"test_btn": "परीक्षण",
|
||||
"save_btn": "सहेजें",
|
||||
"saving": "सहेज रहे हैं…",
|
||||
"settings_saved": "सेटिंग्स सहेजी गईं — OIDC अब {{status}}",
|
||||
"quota_modal_title": "स्टोरेज कोटा अपडेट",
|
||||
"quota_user_label": "उपयोगकर्ता:",
|
||||
"new_quota": "नया कोटा",
|
||||
"quota_unlimited_hint": "असीमित के लिए 0",
|
||||
"cancel": "रद्द करें",
|
||||
"create_user_title": "नया उपयोगकर्ता बनाएं",
|
||||
"username_label": "उपयोगकर्ता नाम",
|
||||
"username_placeholder": "username",
|
||||
"username_hint": "3–32 अक्षर",
|
||||
"password_label": "पासवर्ड",
|
||||
"password_placeholder": "न्यूनतम 8 अक्षर",
|
||||
"email_label": "ईमेल",
|
||||
"email_optional": "(वैकल्पिक)",
|
||||
"email_placeholder": "user@example.com (खाली होने पर स्वतः)",
|
||||
"role_label": "भूमिका",
|
||||
"role_user": "उपयोगकर्ता",
|
||||
"role_admin": "व्यवस्थापक",
|
||||
"quota_label": "कोटा",
|
||||
"creating": "बना रहे हैं…",
|
||||
"reset_pw_title": "पासवर्ड रीसेट",
|
||||
"new_password_label": "नया पासवर्ड",
|
||||
"resetting": "रीसेट हो रहा है…",
|
||||
"reset_btn": "रीसेट",
|
||||
"confirm_role_change": "भूमिका {{role}} में बदलें?",
|
||||
"confirm_deactivate": "इस उपयोगकर्ता को निष्क्रिय करें?",
|
||||
"confirm_activate": "इस उपयोगकर्ता को सक्रिय करें?",
|
||||
"confirm_delete_user": "उपयोगकर्ता \"{{name}}\" हटाएं? पूर्ववत नहीं होगा!",
|
||||
"confirm_action": "कार्रवाई की पुष्टि",
|
||||
"confirm_yes": "पुष्टि",
|
||||
"confirm_no": "रद्द",
|
||||
"error_username_short": "नाम कम से कम 3 अक्षर",
|
||||
"error_password_short": "पासवर्ड कम से कम 8 अक्षर",
|
||||
"error_generic": "विफल",
|
||||
"error_network": "नेटवर्क त्रुटि: {{message}}",
|
||||
"error_create_user": "उपयोगकर्ता बनाने में विफल"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "प्रोफ़ाइल",
|
||||
"back_to_app": "OxiCloud पर वापस",
|
||||
"loading": "लोड हो रहा है…",
|
||||
"not_authenticated": "प्रमाणित नहीं",
|
||||
"not_authenticated_desc": "अपना प्रोफ़ाइल देखने के लिए साइन इन करें।",
|
||||
"sign_in": "साइन इन",
|
||||
"role_admin": "व्यवस्थापक",
|
||||
"role_user": "उपयोगकर्ता",
|
||||
"account_details": "खाता विवरण",
|
||||
"username": "उपयोगकर्ता नाम",
|
||||
"email": "ईमेल",
|
||||
"role": "भूमिका",
|
||||
"last_login": "अंतिम लॉगिन",
|
||||
"storage": "स्टोरेज",
|
||||
"used": "उपयोग किया",
|
||||
"quota": "कोटा",
|
||||
"usage": "उपयोग",
|
||||
"unlimited": "असीमित",
|
||||
"app_passwords": "ऐप पासवर्ड",
|
||||
"app_pw_desc": "WebDAV, CalDAV और CardDAV क्लाइंट के लिए पासवर्ड जनरेट करें। प्रत्येक पासवर्ड केवल एक बार दिखाया जाता है।",
|
||||
"app_pw_label_placeholder": "लेबल (जैसे Thunderbird, macOS)",
|
||||
"generate": "जनरेट करें",
|
||||
"generating": "जनरेट हो रहा है…",
|
||||
"new_password_for": "नया पासवर्ड",
|
||||
"copy_warning": "इस पासवर्ड को अभी कॉपी करें। आप इसे दोबारा नहीं देख पाएंगे।",
|
||||
"copy_to_clipboard": "क्लिपबोर्ड पर कॉपी करें",
|
||||
"col_label": "लेबल",
|
||||
"col_created": "बनाया गया",
|
||||
"col_last_used": "अंतिम उपयोग",
|
||||
"col_status": "स्थिति",
|
||||
"active": "सक्रिय",
|
||||
"revoked": "रद्द",
|
||||
"revoke_title": "रद्द करें",
|
||||
"no_app_passwords": "अभी तक कोई ऐप पासवर्ड नहीं।",
|
||||
"client_sessions": "क्लाइंट सत्र",
|
||||
"client_sessions_desc": "Nextcloud-संगत क्लाइंट कनेक्ट करने पर स्वतः जनरेट।",
|
||||
"col_client": "क्लाइंट",
|
||||
"never": "कभी नहीं",
|
||||
"just_now": "अभी",
|
||||
"minutes_ago": "{{n}} मिनट पहले",
|
||||
"hours_ago": "{{n}} घंटे पहले",
|
||||
"days_ago": "{{n}} दिन पहले",
|
||||
"change_password": "पासवर्ड बदलें",
|
||||
"current_password": "वर्तमान पासवर्ड",
|
||||
"new_password": "नया पासवर्ड",
|
||||
"min_8_chars": "कम से कम 8 अक्षर",
|
||||
"confirm_password": "नया पासवर्ड पुष्टि करें",
|
||||
"update_password": "पासवर्ड अपडेट करें",
|
||||
"updating": "अपडेट हो रहा है…",
|
||||
"password_updated": "पासवर्ड सफलतापूर्वक अपडेट हुआ",
|
||||
"passwords_no_match": "पासवर्ड मेल नहीं खाते",
|
||||
"password_too_short": "पासवर्ड कम से कम 8 अक्षर का होना चाहिए",
|
||||
"password_change_failed": "पासवर्ड बदलने में विफल",
|
||||
"error_network": "नेटवर्क त्रुटि: {{message}}",
|
||||
"error_label_required": "कृपया एक लेबल दर्ज करें",
|
||||
"error_create_pw": "ऐप पासवर्ड बनाने में विफल",
|
||||
"confirm_revoke": "ऐप पासवर्ड \"{{label}}\" रद्द करें? इसका उपयोग करने वाले क्लाइंट काम करना बंद कर देंगे।",
|
||||
"error_revoke": "रद्द करने में विफल"
|
||||
}
|
||||
}
|
||||
+182
-2
@@ -182,6 +182,7 @@
|
||||
"size": "Dimensione",
|
||||
"modified": "Modificato",
|
||||
"no_files": "Nessun file in questa cartella",
|
||||
"empty_hint": "Carica file o crea cartelle per iniziare",
|
||||
"loading": "Caricamento file…",
|
||||
"view_grid": "Visualizzazione griglia",
|
||||
"view_list": "Visualizzazione elenco",
|
||||
@@ -317,8 +318,8 @@
|
||||
"zoom_reset": "Reimposta zoom"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "Benvenuto in OxiCloud",
|
||||
"subtitle": "Seleziona la tua lingua",
|
||||
"title": "Benvenuto!",
|
||||
"subtitle": "Seleziona la tua lingua per continuare",
|
||||
"continue": "Continua",
|
||||
"languages": {
|
||||
"en": "Inglese",
|
||||
@@ -366,5 +367,184 @@
|
||||
"move_title": "Sposta {{count}} elemento/i",
|
||||
"add_favorites": "Aggiungi ai preferiti",
|
||||
"move_copy": "Sposta o copia"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "Pannello di Amministrazione",
|
||||
"back_to_app": "Torna a OxiCloud",
|
||||
"loading": "Caricamento…",
|
||||
"access_denied": "Accesso Negato",
|
||||
"access_denied_desc": "Privilegi di amministratore necessari.",
|
||||
"sign_in": "Accedi",
|
||||
"tab_dashboard": "Dashboard",
|
||||
"tab_users": "Utenti",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "Utenti Totali",
|
||||
"active_users": "Utenti Attivi",
|
||||
"admins": "Amministratori",
|
||||
"version": "Versione",
|
||||
"storage_overview": "Panoramica Archiviazione",
|
||||
"used": "Usato",
|
||||
"total_quota": "Quota Totale",
|
||||
"usage_pct": "Utilizzo %",
|
||||
"users_over_80": "Utenti >80% quota",
|
||||
"users_over_quota": "Utenti oltre la quota",
|
||||
"system": "Sistema",
|
||||
"auth_label": "Auth",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "Quote",
|
||||
"enabled": "Abilitato",
|
||||
"disabled": "Disabilitato",
|
||||
"active": "Attivo",
|
||||
"off": "Spento",
|
||||
"allow_registration": "Consenti registrazione pubblica",
|
||||
"registration_warning": "La registrazione pubblica è disabilitata. Solo gli admin possono creare utenti.",
|
||||
"user_management": "Gestione Utenti",
|
||||
"create_user": "Crea Utente",
|
||||
"col_user": "Utente",
|
||||
"col_role": "Ruolo",
|
||||
"col_auth": "Auth",
|
||||
"col_status": "Stato",
|
||||
"col_storage": "Archiviazione",
|
||||
"col_last_login": "Ultimo Accesso",
|
||||
"col_actions": "Azioni",
|
||||
"loading_users": "Caricamento utenti…",
|
||||
"failed_load_users": "Impossibile caricare",
|
||||
"no_users_found": "Nessun utente trovato",
|
||||
"showing_users": "Mostrando {{from}}-{{to}} di {{total}}",
|
||||
"prev": "Precedente",
|
||||
"next": "Successivo",
|
||||
"inactive": "Inattivo",
|
||||
"you_badge": "(tu)",
|
||||
"local": "Locale",
|
||||
"never": "Mai",
|
||||
"just_now": "Proprio adesso",
|
||||
"minutes_ago": "{{n}}min fa",
|
||||
"hours_ago": "{{n}}h fa",
|
||||
"days_ago": "{{n}}g fa",
|
||||
"edit_quota_title": "Modifica quota",
|
||||
"reset_password_title": "Reimposta password",
|
||||
"toggle_role_title": "Cambia ruolo",
|
||||
"deactivate_title": "Disattiva",
|
||||
"activate_title": "Attiva",
|
||||
"delete_title": "Elimina",
|
||||
"sso_title": "Single Sign-On (OIDC / SSO)",
|
||||
"enable_sso": "Abilita autenticazione SSO",
|
||||
"provider_name": "Nome Provider",
|
||||
"issuer_url": "URL Emittente",
|
||||
"issuer_url_hint": "URL dell'emittente OpenID Connect",
|
||||
"auto_discover": "Auto-scoperta",
|
||||
"discovering": "Scoperta…",
|
||||
"client_id": "Client ID",
|
||||
"client_secret": "Client Secret",
|
||||
"client_secret_placeholder": "Lascia vuoto per mantenere il valore",
|
||||
"secret_configured": "Un client secret è già configurato",
|
||||
"callback_url": "URL di Callback",
|
||||
"callback_url_hint": "(registra nel tuo IdP)",
|
||||
"advanced_settings": "Impostazioni Avanzate",
|
||||
"scopes": "Scopes",
|
||||
"auto_provision": "Provisioning automatico degli utenti",
|
||||
"admin_groups": "Gruppi Admin",
|
||||
"admin_groups_hint": "Nomi di gruppi OIDC separati da virgola",
|
||||
"disable_password": "Disabilita accesso con password (solo OIDC)",
|
||||
"password_warning": "Questo impedirà TUTTI gli accessi tramite password!",
|
||||
"test_btn": "Test",
|
||||
"save_btn": "Salva",
|
||||
"saving": "Salvataggio…",
|
||||
"settings_saved": "Impostazioni salvate — OIDC ora è {{status}}",
|
||||
"quota_modal_title": "Aggiorna Quota",
|
||||
"quota_user_label": "Utente:",
|
||||
"new_quota": "Nuova Quota",
|
||||
"quota_unlimited_hint": "0 per illimitato",
|
||||
"cancel": "Annulla",
|
||||
"create_user_title": "Crea Nuovo Utente",
|
||||
"username_label": "Nome utente",
|
||||
"username_placeholder": "mariorossi",
|
||||
"username_hint": "3–32 caratteri",
|
||||
"password_label": "Password",
|
||||
"password_placeholder": "Min 8 caratteri",
|
||||
"email_label": "Email",
|
||||
"email_optional": "(facoltativo)",
|
||||
"email_placeholder": "utente@esempio.com (auto-generata se vuoto)",
|
||||
"role_label": "Ruolo",
|
||||
"role_user": "Utente",
|
||||
"role_admin": "Admin",
|
||||
"quota_label": "Quota",
|
||||
"creating": "Creazione…",
|
||||
"reset_pw_title": "Reimposta Password",
|
||||
"new_password_label": "Nuova Password",
|
||||
"resetting": "Reimpostazione…",
|
||||
"reset_btn": "Reimposta",
|
||||
"confirm_role_change": "Cambiare ruolo a {{role}}?",
|
||||
"confirm_deactivate": "Sei sicuro di voler disattivare questo utente?",
|
||||
"confirm_activate": "Sei sicuro di voler attivare questo utente?",
|
||||
"confirm_delete_user": "ELIMINARE l'utente \"{{name}}\"? Azione irreversibile!",
|
||||
"confirm_action": "Conferma Azione",
|
||||
"confirm_yes": "Conferma",
|
||||
"confirm_no": "Annulla",
|
||||
"error_username_short": "Il nome utente deve avere almeno 3 caratteri",
|
||||
"error_password_short": "La password deve avere almeno 8 caratteri",
|
||||
"error_generic": "Fallito",
|
||||
"error_network": "Errore di rete: {{message}}",
|
||||
"error_create_user": "Impossibile creare l'utente"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "Profilo",
|
||||
"back_to_app": "Torna a OxiCloud",
|
||||
"loading": "Caricamento…",
|
||||
"not_authenticated": "Non Autenticato",
|
||||
"not_authenticated_desc": "Accedi per visualizzare il tuo profilo.",
|
||||
"sign_in": "Accedi",
|
||||
"role_admin": "Amministratore",
|
||||
"role_user": "Utente",
|
||||
"account_details": "Dettagli Account",
|
||||
"username": "Nome utente",
|
||||
"email": "Email",
|
||||
"role": "Ruolo",
|
||||
"last_login": "Ultimo accesso",
|
||||
"storage": "Archiviazione",
|
||||
"used": "Usato",
|
||||
"quota": "Quota",
|
||||
"usage": "Utilizzo",
|
||||
"unlimited": "Illimitato",
|
||||
"app_passwords": "Password Applicazione",
|
||||
"app_pw_desc": "Genera password per client WebDAV, CalDAV e CardDAV. Ogni password viene mostrata una sola volta.",
|
||||
"app_pw_label_placeholder": "Etichetta (es. Thunderbird, macOS)",
|
||||
"generate": "Genera",
|
||||
"generating": "Generazione…",
|
||||
"new_password_for": "Nuova password per",
|
||||
"copy_warning": "Copia questa password ora. Non potrai rivederla.",
|
||||
"copy_to_clipboard": "Copia negli appunti",
|
||||
"col_label": "Etichetta",
|
||||
"col_created": "Creato",
|
||||
"col_last_used": "Ultimo utilizzo",
|
||||
"col_status": "Stato",
|
||||
"active": "Attiva",
|
||||
"revoked": "Revocata",
|
||||
"revoke_title": "Revoca",
|
||||
"no_app_passwords": "Nessuna password applicazione ancora.",
|
||||
"client_sessions": "Sessioni client",
|
||||
"client_sessions_desc": "Generate automaticamente quando connetti un client compatibile Nextcloud.",
|
||||
"col_client": "Client",
|
||||
"never": "Mai",
|
||||
"just_now": "Proprio adesso",
|
||||
"minutes_ago": "{{n}} min fa",
|
||||
"hours_ago": "{{n}}h fa",
|
||||
"days_ago": "{{n}} giorni fa",
|
||||
"change_password": "Cambia Password",
|
||||
"current_password": "Password Attuale",
|
||||
"new_password": "Nuova Password",
|
||||
"min_8_chars": "Almeno 8 caratteri",
|
||||
"confirm_password": "Conferma Nuova Password",
|
||||
"update_password": "Aggiorna Password",
|
||||
"updating": "Aggiornamento…",
|
||||
"password_updated": "Password aggiornata con successo",
|
||||
"passwords_no_match": "Le password non corrispondono",
|
||||
"password_too_short": "La password deve avere almeno 8 caratteri",
|
||||
"password_change_failed": "Impossibile cambiare la password",
|
||||
"error_network": "Errore di rete: {{message}}",
|
||||
"error_label_required": "Inserisci un'etichetta",
|
||||
"error_create_pw": "Impossibile creare la password",
|
||||
"confirm_revoke": "Revocare la password \"{{label}}\"? I client che la usano smetteranno di funzionare.",
|
||||
"error_revoke": "Revoca fallita"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
{
|
||||
"app": {
|
||||
"title": "OxiCloud",
|
||||
"description": "ミニマリストクラウドストレージシステム"
|
||||
},
|
||||
"nav": {
|
||||
"files": "ファイル",
|
||||
"shared": "共有",
|
||||
"recent": "最近",
|
||||
"favorites": "お気に入り",
|
||||
"photos": "写真",
|
||||
"trash": "ゴミ箱"
|
||||
},
|
||||
"photos": {
|
||||
"empty_state": "写真はまだありません",
|
||||
"empty_hint": "画像や動画をアップロードするとここに表示されます",
|
||||
"items_selected": "件選択中",
|
||||
"view_daily": "日",
|
||||
"view_monthly": "月",
|
||||
"view_yearly": "年"
|
||||
},
|
||||
"actions": {
|
||||
"search": "ファイルを検索...",
|
||||
"new_folder": "新しいフォルダ",
|
||||
"upload": "アップロード",
|
||||
"upload_files": "ファイルをアップロード",
|
||||
"upload_folder": "フォルダをアップロード",
|
||||
"upload.uploading": "アップロード中...",
|
||||
"upload.complete": "{count} / {total} アップロード完了",
|
||||
"rename": "名前を変更",
|
||||
"move": "移動先...",
|
||||
"move_to": "移動先",
|
||||
"delete": "削除",
|
||||
"download": "ダウンロード",
|
||||
"view": "表示",
|
||||
"cancel": "キャンセル",
|
||||
"confirm": "確認",
|
||||
"share": "共有",
|
||||
"favorite": "お気に入りに追加",
|
||||
"unfavorite": "お気に入りから削除",
|
||||
"copy": "コピー",
|
||||
"notify": "通知",
|
||||
"send": "送信",
|
||||
"clear_recent": "最近をクリア",
|
||||
"logout": "ログアウト",
|
||||
"create": "作成",
|
||||
"search_btn": "検索",
|
||||
"close": "閉じる",
|
||||
"delete_permanently": "完全に削除",
|
||||
"empty_trash": "ゴミ箱を空にする"
|
||||
},
|
||||
"user_menu": {
|
||||
"appearance": "外観",
|
||||
"about": "OxiCloudについて",
|
||||
"about_description": "RustとClean Architectureで構築されたクラウドストレージプラットフォーム。高速・安全・プライベート。",
|
||||
"admin_panel": "管理パネル",
|
||||
"profile": "マイプロフィール",
|
||||
"role_user": "ユーザー"
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "共有リンク",
|
||||
"linkLabel": "共有リンク:",
|
||||
"copyLink": "コピー",
|
||||
"permissions": "権限:",
|
||||
"permissionRead": "読み取り",
|
||||
"permissionWrite": "書き込み",
|
||||
"permissionReshare": "再共有",
|
||||
"password": "パスワード保護:",
|
||||
"generatePassword": "生成",
|
||||
"expiration": "有効期限:",
|
||||
"update": "共有を更新",
|
||||
"remove": "共有を削除",
|
||||
"notifyTitle": "通知を送信",
|
||||
"notifyEmailLabel": "メールアドレス:",
|
||||
"notifyMessageLabel": "メッセージ(任意):",
|
||||
"notifySend": "通知を送信",
|
||||
"shareWithOthers": "他のユーザーと共有",
|
||||
"sharePublicly": "公開共有",
|
||||
"shareSettings": "共有設定",
|
||||
"shareCopied": "リンクがクリップボードにコピーされました",
|
||||
"shareCreated": "共有リンクが正常に作成されました",
|
||||
"shareUpdated": "共有設定が正常に更新されました",
|
||||
"shareRemoved": "共有が正常に削除されました"
|
||||
},
|
||||
"share_dialogTitle": "共有リンク",
|
||||
"share_linkLabel": "共有リンク:",
|
||||
"share_copyLink": "コピー",
|
||||
"share_permissions": "権限:",
|
||||
"share_permissionRead": "読み取り",
|
||||
"share_permissionWrite": "書き込み",
|
||||
"share_permissionReshare": "再共有",
|
||||
"share_password": "パスワード保護:",
|
||||
"share_generatePassword": "生成",
|
||||
"share_expiration": "有効期限:",
|
||||
"share_update": "共有を更新",
|
||||
"share_remove": "共有を削除",
|
||||
"share_notifyTitle": "通知を送信",
|
||||
"share_notifyEmailLabel": "メールアドレス:",
|
||||
"share_notifyMessageLabel": "メッセージ(任意):",
|
||||
"share_notifySend": "通知を送信",
|
||||
"shared": {
|
||||
"backToFiles": "ファイルに戻る",
|
||||
"pageTitle": "共有リソース",
|
||||
"pageDescription": "共有ファイルとフォルダの管理",
|
||||
"filterType": "種類:",
|
||||
"filterAll": "すべて",
|
||||
"filterFiles": "ファイル",
|
||||
"filterFolders": "フォルダ",
|
||||
"sortBy": "並び替え:",
|
||||
"sortByName": "名前",
|
||||
"sortByDate": "共有日",
|
||||
"sortByExpiration": "有効期限",
|
||||
"search": "検索",
|
||||
"colName": "名前",
|
||||
"colType": "種類",
|
||||
"colDateShared": "共有日",
|
||||
"colExpiration": "有効期限",
|
||||
"colPermissions": "権限",
|
||||
"colPassword": "パスワード",
|
||||
"colActions": "操作",
|
||||
"emptyStateTitle": "共有リソースはまだありません",
|
||||
"emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます",
|
||||
"goToFiles": "ファイルへ移動",
|
||||
"typeFile": "ファイル",
|
||||
"typeFolder": "フォルダ",
|
||||
"noExpiration": "期限なし",
|
||||
"hasPassword": "あり",
|
||||
"noPassword": "なし",
|
||||
"editShare": "共有を編集",
|
||||
"notifyShare": "通知する",
|
||||
"copyLink": "リンクをコピー",
|
||||
"removeShare": "共有を削除",
|
||||
"linkCopied": "リンクがクリップボードにコピーされました!",
|
||||
"linkCopyFailed": "リンクのコピーに失敗しました",
|
||||
"itemUpdated": "共有設定が正常に更新されました",
|
||||
"itemRemoved": "共有が正常に削除されました",
|
||||
"invalidEmail": "有効なメールアドレスを入力してください",
|
||||
"notificationSent": "通知が正常に送信されました",
|
||||
"notificationFailed": "通知の送信に失敗しました",
|
||||
"shared_backToFiles": "ファイルに戻る",
|
||||
"shared_pageTitle": "共有リソース",
|
||||
"shared_pageDescription": "共有ファイルとフォルダの管理",
|
||||
"shared_filterType": "種類:",
|
||||
"shared_filterAll": "すべて",
|
||||
"shared_filterFiles": "ファイル",
|
||||
"shared_filterFolders": "フォルダ",
|
||||
"shared_sortBy": "並び替え:",
|
||||
"shared_sortByName": "名前",
|
||||
"shared_sortByDate": "共有日",
|
||||
"shared_sortByExpiration": "有効期限",
|
||||
"shared_search": "検索",
|
||||
"shared_colName": "名前",
|
||||
"shared_colType": "種類",
|
||||
"shared_colDateShared": "共有日",
|
||||
"shared_colExpiration": "有効期限",
|
||||
"shared_colPermissions": "権限",
|
||||
"shared_colPassword": "パスワード",
|
||||
"shared_colActions": "操作",
|
||||
"shared_emptyStateTitle": "共有リソースはまだありません",
|
||||
"shared_emptyStateDesc": "ファイルやフォルダを共有すると、ここに表示されます",
|
||||
"shared_goToFiles": "ファイルへ移動",
|
||||
"shared_typeFile": "ファイル",
|
||||
"shared_typeFolder": "フォルダ",
|
||||
"shared_noExpiration": "期限なし",
|
||||
"shared_hasPassword": "あり",
|
||||
"shared_noPassword": "なし",
|
||||
"shared_editShare": "共有を編集",
|
||||
"shared_notifyShare": "通知する",
|
||||
"shared_copyLink": "リンクをコピー",
|
||||
"shared_removeShare": "共有を削除",
|
||||
"shared_linkCopied": "リンクがクリップボードにコピーされました!",
|
||||
"shared_linkCopyFailed": "リンクのコピーに失敗しました",
|
||||
"shared_itemUpdated": "共有設定が正常に更新されました",
|
||||
"shared_itemRemoved": "共有が正常に削除されました",
|
||||
"shared_invalidEmail": "有効なメールアドレスを入力してください",
|
||||
"shared_notificationSent": "通知が正常に送信されました",
|
||||
"shared_notificationFailed": "通知の送信に失敗しました"
|
||||
},
|
||||
"files": {
|
||||
"name": "名前",
|
||||
"type": "種類",
|
||||
"size": "サイズ",
|
||||
"modified": "更新日",
|
||||
"no_files": "このフォルダにファイルはありません",
|
||||
"empty_hint": "ファイルをアップロードするかフォルダを作成して始めましょう",
|
||||
"loading": "ファイルを読み込み中…",
|
||||
"view_grid": "グリッド表示",
|
||||
"view_list": "リスト表示",
|
||||
"file_types": {
|
||||
"document": "ドキュメント",
|
||||
"image": "画像",
|
||||
"video": "動画",
|
||||
"audio": "音声",
|
||||
"pdf": "PDF",
|
||||
"text": "テキスト",
|
||||
"folder": "フォルダ",
|
||||
"spreadsheet": "スプレッドシート",
|
||||
"presentation": "プレゼンテーション",
|
||||
"archive": "アーカイブ",
|
||||
"installer": "インストーラー",
|
||||
"code": "コード"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
"rename_folder": "フォルダ名を変更",
|
||||
"rename_file": "ファイル名を変更",
|
||||
"new_name": "新しい名前",
|
||||
"new_folder_title": "新しいフォルダ",
|
||||
"folder_name": "フォルダ名",
|
||||
"folder_placeholder": "マイフォルダ",
|
||||
"rename_title": "名前を変更",
|
||||
"move_file": "ファイルを移動",
|
||||
"move_folder": "フォルダを移動",
|
||||
"select_destination": "移動先フォルダを選択:",
|
||||
"select_this_folder": "このフォルダを選択",
|
||||
"go_to_parent": ".. (親フォルダ)",
|
||||
"no_subfolders": "サブフォルダなし",
|
||||
"root": "ルート",
|
||||
"delete_confirmation": "本当に削除しますか",
|
||||
"and_contents": "およびすべての内容",
|
||||
"no_undo": "この操作は元に戻せません",
|
||||
"confirm_title": "操作の確認",
|
||||
"confirm_delete": "ゴミ箱に移動",
|
||||
"confirm_delete_file": "ファイル「{{name}}」をゴミ箱に移動しますか?",
|
||||
"confirm_delete_folder": "フォルダ「{{name}}」とそのすべての内容をゴミ箱に移動しますか?",
|
||||
"confirm_permanent_delete": "完全に削除",
|
||||
"confirm_permanent_delete_msg": "このアイテムを完全に削除しますか?この操作は元に戻せません。",
|
||||
"confirm_empty_trash": "ゴミ箱を空にする",
|
||||
"confirm_delete_share": "共有リンクを削除",
|
||||
"confirm_delete_share_msg": "この共有リンクを削除しますか?",
|
||||
"share_file": "ファイルを共有",
|
||||
"existing_shares": "既存の共有",
|
||||
"share_options": "共有オプション",
|
||||
"password": "パスワード",
|
||||
"expiration": "有効期限",
|
||||
"permissions": "権限",
|
||||
"generated_link": "生成されたリンク",
|
||||
"notify": "通知を送信",
|
||||
"recipient": "宛先",
|
||||
"message": "メッセージ"
|
||||
},
|
||||
"dropzone": {
|
||||
"drag_files": "ファイルをここにドラッグするか、クリックして選択",
|
||||
"drop_files": "ファイルをドロップしてアップロード"
|
||||
},
|
||||
"permissions": {
|
||||
"read": "読み取り",
|
||||
"write": "書き込み",
|
||||
"reshare": "再共有"
|
||||
},
|
||||
"errors": {
|
||||
"file_not_found": "ファイルが見つかりません",
|
||||
"folder_not_found": "フォルダが見つかりません",
|
||||
"delete_error": "削除エラー",
|
||||
"upload_error": "ファイルのアップロードエラー",
|
||||
"rename_error": "名前変更エラー",
|
||||
"move_error": "移動エラー",
|
||||
"empty_name": "名前を空にすることはできません",
|
||||
"name_exists": "同じ名前のファイルまたはフォルダが既に存在します",
|
||||
"generic_error": "エラーが発生しました"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"home": "ホーム"
|
||||
},
|
||||
"trash": {
|
||||
"empty_trash": "ゴミ箱を空にする",
|
||||
"empty_state": "ゴミ箱は空です",
|
||||
"original_location": "元の場所",
|
||||
"deleted_date": "削除日",
|
||||
"actions": "操作",
|
||||
"restore": "復元",
|
||||
"delete_permanently": "完全に削除",
|
||||
"empty_confirm": "ゴミ箱を空にしますか?すべてのアイテムが完全に削除されます。"
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "サインイン",
|
||||
"username": "ユーザー名",
|
||||
"username_placeholder": "ユーザー名を入力",
|
||||
"password": "パスワード",
|
||||
"password_placeholder": "パスワードを入力",
|
||||
"login_button": "サインイン",
|
||||
"no_account": "アカウントをお持ちでないですか?",
|
||||
"register": "登録",
|
||||
"admin_setup": "初回ですか?",
|
||||
"setup": "管理者をセットアップ",
|
||||
"register_title": "アカウント作成",
|
||||
"email": "メール",
|
||||
"email_placeholder": "メールアドレスを入力",
|
||||
"confirm_password": "パスワードの確認",
|
||||
"confirm_password_placeholder": "パスワードを再入力",
|
||||
"register_button": "アカウント作成",
|
||||
"have_account": "既にアカウントをお持ちですか?",
|
||||
"login": "サインイン",
|
||||
"setup_title": "初期設定",
|
||||
"setup_step1": "管理者",
|
||||
"setup_step2": "システム",
|
||||
"setup_step3": "完了",
|
||||
"admin_username": "管理者ユーザー名",
|
||||
"admin_email": "管理者メール",
|
||||
"admin_password": "管理者パスワード",
|
||||
"create_admin": "管理者を作成",
|
||||
"back_to_login": "設定済みですか?",
|
||||
"admin_success": "管理者アカウントが正常に作成されました!サインインできます。",
|
||||
"account_success": "アカウントが正常に作成されました!サインインできます。",
|
||||
"passwords_mismatch": "パスワードが一致しません",
|
||||
"admin_create_error": "管理者アカウントの作成エラー",
|
||||
"or": "または",
|
||||
"sso_login": "SSOでサインイン",
|
||||
"sso_login_provider": "{{provider}}でサインイン"
|
||||
},
|
||||
"storage": {
|
||||
"title": "ストレージ",
|
||||
"calculating": "計算中...",
|
||||
"used": "{{percentage}}% 使用中 ({{used}} / {{total}})"
|
||||
},
|
||||
"viewer": {
|
||||
"unsupported_file": "このファイル形式はプレビューできません。",
|
||||
"download_file": "ファイルをダウンロード",
|
||||
"zoom_in": "拡大",
|
||||
"zoom_out": "縮小",
|
||||
"zoom_reset": "ズームリセット"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "ようこそ!",
|
||||
"subtitle": "続行するには言語を選択してください",
|
||||
"continue": "続行",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"zh": "中文",
|
||||
"fa": "فارسی",
|
||||
"fr": "Français",
|
||||
"de": "Deutsch",
|
||||
"pt": "Português",
|
||||
"ja": "日本語"
|
||||
}
|
||||
},
|
||||
"favorites": {
|
||||
"empty_state": "お気に入りはまだありません",
|
||||
"empty_hint": "ファイルやフォルダにスターを付けてお気に入りに追加",
|
||||
"add": "お気に入りに追加",
|
||||
"remove": "お気に入りから削除",
|
||||
"added_title": "お気に入りに追加しました",
|
||||
"added_msg": "お気に入りに追加しました",
|
||||
"removed_title": "お気に入りから削除しました",
|
||||
"removed_msg": "お気に入りから削除しました"
|
||||
},
|
||||
"recent": {
|
||||
"title": "最近",
|
||||
"clear": "最近をクリア",
|
||||
"accessed": "アクセス日",
|
||||
"empty_state": "最近のファイルはありません",
|
||||
"empty_hint": "開いたファイルがここに表示されます"
|
||||
},
|
||||
"notifications": {
|
||||
"file_renamed": "ファイル名を変更しました",
|
||||
"file_renamed_to": "ファイル名を「{{name}}」に変更しました",
|
||||
"folder_renamed": "フォルダ名を変更しました",
|
||||
"folder_renamed_to": "フォルダ名を「{{name}}」に変更しました",
|
||||
"file_uploaded": "ファイルをアップロードしました",
|
||||
"file_deleted": "ファイルをゴミ箱に移動しました",
|
||||
"folder_deleted": "フォルダをゴミ箱に移動しました",
|
||||
"item_deleted_permanently": "アイテムを完全に削除しました",
|
||||
"trash_emptied": "ゴミ箱を正常に空にしました",
|
||||
"title": "通知",
|
||||
"empty": "通知はありません"
|
||||
},
|
||||
"batch": {
|
||||
"one_selected": "1件選択中",
|
||||
"n_selected": "{{count}}件選択中",
|
||||
"confirm_delete": "{{count}}件のアイテムをゴミ箱に移動しますか?",
|
||||
"move_title": "{{count}}件のアイテムを移動",
|
||||
"add_favorites": "お気に入りに追加",
|
||||
"move_copy": "移動またはコピー"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "管理パネル",
|
||||
"back_to_app": "OxiCloudに戻る",
|
||||
"loading": "読み込み中…",
|
||||
"access_denied": "アクセス拒否",
|
||||
"access_denied_desc": "管理者権限が必要です。",
|
||||
"sign_in": "サインイン",
|
||||
"tab_dashboard": "ダッシュボード",
|
||||
"tab_users": "ユーザー",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "総ユーザー数",
|
||||
"active_users": "アクティブ",
|
||||
"admins": "管理者",
|
||||
"version": "バージョン",
|
||||
"storage_overview": "ストレージ概要",
|
||||
"used": "使用済み",
|
||||
"total_quota": "合計クォータ",
|
||||
"usage_pct": "使用率",
|
||||
"users_over_80": "クォータ80%超",
|
||||
"users_over_quota": "クォータ超過",
|
||||
"system": "システム",
|
||||
"auth_label": "認証",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "クォータ",
|
||||
"enabled": "有効",
|
||||
"disabled": "無効",
|
||||
"active": "アクティブ",
|
||||
"off": "オフ",
|
||||
"allow_registration": "公開セルフ登録を許可",
|
||||
"registration_warning": "公開登録は無効です。管理者のみがユーザーを作成できます。",
|
||||
"user_management": "ユーザー管理",
|
||||
"create_user": "ユーザー作成",
|
||||
"col_user": "ユーザー",
|
||||
"col_role": "役割",
|
||||
"col_auth": "認証",
|
||||
"col_status": "ステータス",
|
||||
"col_storage": "ストレージ",
|
||||
"col_last_login": "最終ログイン",
|
||||
"col_actions": "操作",
|
||||
"loading_users": "ユーザーを読み込み中…",
|
||||
"failed_load_users": "読み込み失敗",
|
||||
"no_users_found": "ユーザーなし",
|
||||
"showing_users": "{{from}}-{{to}} / {{total}} を表示",
|
||||
"prev": "前へ",
|
||||
"next": "次へ",
|
||||
"inactive": "非アクティブ",
|
||||
"you_badge": "(あなた)",
|
||||
"local": "ローカル",
|
||||
"never": "未ログイン",
|
||||
"just_now": "たった今",
|
||||
"minutes_ago": "{{n}}分前",
|
||||
"hours_ago": "{{n}}時間前",
|
||||
"days_ago": "{{n}}日前",
|
||||
"edit_quota_title": "クォータを編集",
|
||||
"reset_password_title": "パスワードリセット",
|
||||
"toggle_role_title": "役割を切替",
|
||||
"deactivate_title": "無効化",
|
||||
"activate_title": "有効化",
|
||||
"delete_title": "削除",
|
||||
"sso_title": "シングルサインオン (OIDC / SSO)",
|
||||
"enable_sso": "SSO認証を有効化",
|
||||
"provider_name": "プロバイダー名",
|
||||
"issuer_url": "発行者URL",
|
||||
"issuer_url_hint": "OpenID Connect発行者URL",
|
||||
"auto_discover": "自動検出",
|
||||
"discovering": "検出中…",
|
||||
"client_id": "クライアントID",
|
||||
"client_secret": "クライアントシークレット",
|
||||
"client_secret_placeholder": "現在の値を維持するには空に",
|
||||
"secret_configured": "クライアントシークレット設定済み",
|
||||
"callback_url": "コールバックURL",
|
||||
"callback_url_hint": "(IdPに登録)",
|
||||
"advanced_settings": "詳細設定",
|
||||
"scopes": "スコープ",
|
||||
"auto_provision": "初回ログイン時に自動プロビジョニング",
|
||||
"admin_groups": "管理者グループ",
|
||||
"admin_groups_hint": "カンマ区切りのOIDCグループ名",
|
||||
"disable_password": "パスワードログイン無効化(OIDCのみ)",
|
||||
"password_warning": "すべてのパスワードログインが無効に!",
|
||||
"test_btn": "テスト",
|
||||
"save_btn": "保存",
|
||||
"saving": "保存中…",
|
||||
"settings_saved": "設定が保存されました — OIDC: {{status}}",
|
||||
"quota_modal_title": "ストレージクォータ更新",
|
||||
"quota_user_label": "ユーザー:",
|
||||
"new_quota": "新しいクォータ",
|
||||
"quota_unlimited_hint": "0で無制限",
|
||||
"cancel": "キャンセル",
|
||||
"create_user_title": "新規ユーザー作成",
|
||||
"username_label": "ユーザー名",
|
||||
"username_placeholder": "taro",
|
||||
"username_hint": "3〜32文字",
|
||||
"password_label": "パスワード",
|
||||
"password_placeholder": "8文字以上",
|
||||
"email_label": "メール",
|
||||
"email_optional": "(任意)",
|
||||
"email_placeholder": "user@example.com(空なら自動生成)",
|
||||
"role_label": "役割",
|
||||
"role_user": "ユーザー",
|
||||
"role_admin": "管理者",
|
||||
"quota_label": "クォータ",
|
||||
"creating": "作成中…",
|
||||
"reset_pw_title": "パスワードリセット",
|
||||
"new_password_label": "新しいパスワード",
|
||||
"resetting": "リセット中…",
|
||||
"reset_btn": "リセット",
|
||||
"confirm_role_change": "役割を{{role}}に変更?",
|
||||
"confirm_deactivate": "このユーザーを無効化しますか?",
|
||||
"confirm_activate": "このユーザーを有効化しますか?",
|
||||
"confirm_delete_user": "ユーザー「{{name}}」を削除?取り消せません!",
|
||||
"confirm_action": "操作の確認",
|
||||
"confirm_yes": "確認",
|
||||
"confirm_no": "キャンセル",
|
||||
"error_username_short": "ユーザー名は3文字以上",
|
||||
"error_password_short": "パスワードは8文字以上",
|
||||
"error_generic": "失敗",
|
||||
"error_network": "ネットワークエラー: {{message}}",
|
||||
"error_create_user": "ユーザー作成失敗"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "プロフィール",
|
||||
"back_to_app": "OxiCloudに戻る",
|
||||
"loading": "読み込み中…",
|
||||
"not_authenticated": "未認証",
|
||||
"not_authenticated_desc": "プロフィールを表示するにはサインインしてください。",
|
||||
"sign_in": "サインイン",
|
||||
"role_admin": "管理者",
|
||||
"role_user": "ユーザー",
|
||||
"account_details": "アカウント詳細",
|
||||
"username": "ユーザー名",
|
||||
"email": "メール",
|
||||
"role": "役割",
|
||||
"last_login": "最終ログイン",
|
||||
"storage": "ストレージ",
|
||||
"used": "使用済み",
|
||||
"quota": "クォータ",
|
||||
"usage": "使用率",
|
||||
"unlimited": "無制限",
|
||||
"app_passwords": "アプリパスワード",
|
||||
"app_pw_desc": "WebDAV、CalDAV、CardDAVクライアント用のパスワードを生成します。各パスワードは一度だけ表示されます。",
|
||||
"app_pw_label_placeholder": "ラベル(例:Thunderbird、macOS)",
|
||||
"generate": "生成",
|
||||
"generating": "生成中…",
|
||||
"new_password_for": "新しいパスワード:",
|
||||
"copy_warning": "このパスワードを今コピーしてください。再度表示できません。",
|
||||
"copy_to_clipboard": "クリップボードにコピー",
|
||||
"col_label": "ラベル",
|
||||
"col_created": "作成日",
|
||||
"col_last_used": "最終使用",
|
||||
"col_status": "ステータス",
|
||||
"active": "アクティブ",
|
||||
"revoked": "失効済み",
|
||||
"revoke_title": "失効",
|
||||
"no_app_passwords": "アプリパスワードはまだありません。",
|
||||
"client_sessions": "クライアントセッション",
|
||||
"client_sessions_desc": "Nextcloud互換クライアント接続時に自動生成されます。",
|
||||
"col_client": "クライアント",
|
||||
"never": "未ログイン",
|
||||
"just_now": "たった今",
|
||||
"minutes_ago": "{{n}}分前",
|
||||
"hours_ago": "{{n}}時間前",
|
||||
"days_ago": "{{n}}日前",
|
||||
"change_password": "パスワード変更",
|
||||
"current_password": "現在のパスワード",
|
||||
"new_password": "新しいパスワード",
|
||||
"min_8_chars": "8文字以上",
|
||||
"confirm_password": "新しいパスワードの確認",
|
||||
"update_password": "パスワードを更新",
|
||||
"updating": "更新中…",
|
||||
"password_updated": "パスワードが正常に更新されました",
|
||||
"passwords_no_match": "パスワードが一致しません",
|
||||
"password_too_short": "パスワードは8文字以上必要です",
|
||||
"password_change_failed": "パスワードの変更に失敗しました",
|
||||
"error_network": "ネットワークエラー: {{message}}",
|
||||
"error_label_required": "ラベルを入力してください",
|
||||
"error_create_pw": "アプリパスワードの作成に失敗しました",
|
||||
"confirm_revoke": "アプリパスワード「{{label}}」を失効させますか?使用中のクライアントは動作しなくなります。",
|
||||
"error_revoke": "失効に失敗しました"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,555 @@
|
||||
{
|
||||
"app": {
|
||||
"title": "OxiCloud",
|
||||
"description": "미니멀리스트 클라우드 스토리지 시스템"
|
||||
},
|
||||
"nav": {
|
||||
"files": "파일",
|
||||
"shared": "공유",
|
||||
"recent": "최근",
|
||||
"favorites": "즐겨찾기",
|
||||
"photos": "사진",
|
||||
"trash": "휴지통"
|
||||
},
|
||||
"photos": {
|
||||
"empty_state": "아직 사진이 없습니다",
|
||||
"empty_hint": "이미지나 동영상을 업로드하면 여기에 표시됩니다",
|
||||
"items_selected": "개 선택됨",
|
||||
"view_daily": "일",
|
||||
"view_monthly": "월",
|
||||
"view_yearly": "년"
|
||||
},
|
||||
"actions": {
|
||||
"search": "파일 검색...",
|
||||
"new_folder": "새 폴더",
|
||||
"upload": "업로드",
|
||||
"upload_files": "파일 업로드",
|
||||
"upload_folder": "폴더 업로드",
|
||||
"upload.uploading": "업로드 중...",
|
||||
"upload.complete": "{count} / {total} 업로드 완료",
|
||||
"rename": "이름 변경",
|
||||
"move": "이동...",
|
||||
"move_to": "이동 대상",
|
||||
"delete": "삭제",
|
||||
"download": "다운로드",
|
||||
"view": "보기",
|
||||
"cancel": "취소",
|
||||
"confirm": "확인",
|
||||
"share": "공유",
|
||||
"favorite": "즐겨찾기 추가",
|
||||
"unfavorite": "즐겨찾기 해제",
|
||||
"copy": "복사",
|
||||
"notify": "알림",
|
||||
"send": "보내기",
|
||||
"clear_recent": "최근 항목 지우기",
|
||||
"logout": "로그아웃",
|
||||
"create": "만들기",
|
||||
"search_btn": "검색",
|
||||
"close": "닫기",
|
||||
"delete_permanently": "영구 삭제",
|
||||
"empty_trash": "휴지통 비우기"
|
||||
},
|
||||
"user_menu": {
|
||||
"appearance": "외관",
|
||||
"about": "OxiCloud 정보",
|
||||
"about_description": "Rust와 Clean Architecture로 구축된 클라우드 스토리지 플랫폼. 빠르고, 안전하고, 프라이빗합니다.",
|
||||
"admin_panel": "관리자 패널",
|
||||
"profile": "내 프로필",
|
||||
"role_user": "사용자"
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "공유 링크",
|
||||
"linkLabel": "공유 링크:",
|
||||
"copyLink": "복사",
|
||||
"permissions": "권한:",
|
||||
"permissionRead": "읽기",
|
||||
"permissionWrite": "쓰기",
|
||||
"permissionReshare": "재공유",
|
||||
"password": "비밀번호 보호:",
|
||||
"generatePassword": "생성",
|
||||
"expiration": "만료일:",
|
||||
"update": "공유 업데이트",
|
||||
"remove": "공유 삭제",
|
||||
"notifyTitle": "알림 보내기",
|
||||
"notifyEmailLabel": "이메일 주소:",
|
||||
"notifyMessageLabel": "메시지 (선택사항):",
|
||||
"notifySend": "알림 보내기",
|
||||
"shareWithOthers": "다른 사용자와 공유",
|
||||
"sharePublicly": "공개 공유",
|
||||
"shareSettings": "공유 설정",
|
||||
"shareCopied": "링크가 클립보드에 복사되었습니다",
|
||||
"shareCreated": "공유 링크가 성공적으로 생성되었습니다",
|
||||
"shareUpdated": "공유 설정이 성공적으로 업데이트되었습니다",
|
||||
"shareRemoved": "공유가 성공적으로 삭제되었습니다"
|
||||
},
|
||||
"share_dialogTitle": "공유 링크",
|
||||
"share_linkLabel": "공유 링크:",
|
||||
"share_copyLink": "복사",
|
||||
"share_permissions": "권한:",
|
||||
"share_permissionRead": "읽기",
|
||||
"share_permissionWrite": "쓰기",
|
||||
"share_permissionReshare": "재공유",
|
||||
"share_password": "비밀번호 보호:",
|
||||
"share_generatePassword": "생성",
|
||||
"share_expiration": "만료일:",
|
||||
"share_update": "공유 업데이트",
|
||||
"share_remove": "공유 삭제",
|
||||
"share_notifyTitle": "알림 보내기",
|
||||
"share_notifyEmailLabel": "이메일 주소:",
|
||||
"share_notifyMessageLabel": "메시지 (선택사항):",
|
||||
"share_notifySend": "알림 보내기",
|
||||
"shared": {
|
||||
"backToFiles": "파일로 돌아가기",
|
||||
"pageTitle": "공유 리소스",
|
||||
"pageDescription": "공유 파일 및 폴더 관리",
|
||||
"filterType": "유형:",
|
||||
"filterAll": "전체",
|
||||
"filterFiles": "파일",
|
||||
"filterFolders": "폴더",
|
||||
"sortBy": "정렬:",
|
||||
"sortByName": "이름",
|
||||
"sortByDate": "공유일",
|
||||
"sortByExpiration": "만료일",
|
||||
"search": "검색",
|
||||
"colName": "이름",
|
||||
"colType": "유형",
|
||||
"colDateShared": "공유일",
|
||||
"colExpiration": "만료일",
|
||||
"colPermissions": "권한",
|
||||
"colPassword": "비밀번호",
|
||||
"colActions": "작업",
|
||||
"emptyStateTitle": "아직 공유된 리소스가 없습니다",
|
||||
"emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다",
|
||||
"goToFiles": "파일로 이동",
|
||||
"typeFile": "파일",
|
||||
"typeFolder": "폴더",
|
||||
"noExpiration": "만료 없음",
|
||||
"hasPassword": "있음",
|
||||
"noPassword": "없음",
|
||||
"editShare": "공유 편집",
|
||||
"notifyShare": "알림",
|
||||
"copyLink": "링크 복사",
|
||||
"removeShare": "공유 삭제",
|
||||
"linkCopied": "링크가 클립보드에 복사되었습니다!",
|
||||
"linkCopyFailed": "링크 복사에 실패했습니다",
|
||||
"itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다",
|
||||
"itemRemoved": "공유가 성공적으로 삭제되었습니다",
|
||||
"invalidEmail": "유효한 이메일 주소를 입력하세요",
|
||||
"notificationSent": "알림이 성공적으로 전송되었습니다",
|
||||
"notificationFailed": "알림 전송에 실패했습니다",
|
||||
"shared_backToFiles": "파일로 돌아가기",
|
||||
"shared_pageTitle": "공유 리소스",
|
||||
"shared_pageDescription": "공유 파일 및 폴더 관리",
|
||||
"shared_filterType": "유형:",
|
||||
"shared_filterAll": "전체",
|
||||
"shared_filterFiles": "파일",
|
||||
"shared_filterFolders": "폴더",
|
||||
"shared_sortBy": "정렬:",
|
||||
"shared_sortByName": "이름",
|
||||
"shared_sortByDate": "공유일",
|
||||
"shared_sortByExpiration": "만료일",
|
||||
"shared_search": "검색",
|
||||
"shared_colName": "이름",
|
||||
"shared_colType": "유형",
|
||||
"shared_colDateShared": "공유일",
|
||||
"shared_colExpiration": "만료일",
|
||||
"shared_colPermissions": "권한",
|
||||
"shared_colPassword": "비밀번호",
|
||||
"shared_colActions": "작업",
|
||||
"shared_emptyStateTitle": "아직 공유된 리소스가 없습니다",
|
||||
"shared_emptyStateDesc": "파일이나 폴더를 공유하면 여기에 표시됩니다",
|
||||
"shared_goToFiles": "파일로 이동",
|
||||
"shared_typeFile": "파일",
|
||||
"shared_typeFolder": "폴더",
|
||||
"shared_noExpiration": "만료 없음",
|
||||
"shared_hasPassword": "있음",
|
||||
"shared_noPassword": "없음",
|
||||
"shared_editShare": "공유 편집",
|
||||
"shared_notifyShare": "알림",
|
||||
"shared_copyLink": "링크 복사",
|
||||
"shared_removeShare": "공유 삭제",
|
||||
"shared_linkCopied": "링크가 클립보드에 복사되었습니다!",
|
||||
"shared_linkCopyFailed": "링크 복사에 실패했습니다",
|
||||
"shared_itemUpdated": "공유 설정이 성공적으로 업데이트되었습니다",
|
||||
"shared_itemRemoved": "공유가 성공적으로 삭제되었습니다",
|
||||
"shared_invalidEmail": "유효한 이메일 주소를 입력하세요",
|
||||
"shared_notificationSent": "알림이 성공적으로 전송되었습니다",
|
||||
"shared_notificationFailed": "알림 전송에 실패했습니다"
|
||||
},
|
||||
"files": {
|
||||
"name": "이름",
|
||||
"type": "유형",
|
||||
"size": "크기",
|
||||
"modified": "수정일",
|
||||
"no_files": "이 폴더에 파일이 없습니다",
|
||||
"empty_hint": "파일을 업로드하거나 폴더를 만들어 시작하세요",
|
||||
"loading": "파일 로딩 중…",
|
||||
"view_grid": "그리드 보기",
|
||||
"view_list": "목록 보기",
|
||||
"file_types": {
|
||||
"document": "문서",
|
||||
"image": "이미지",
|
||||
"video": "동영상",
|
||||
"audio": "오디오",
|
||||
"pdf": "PDF",
|
||||
"text": "텍스트",
|
||||
"folder": "폴더",
|
||||
"spreadsheet": "스프레드시트",
|
||||
"presentation": "프레젠테이션",
|
||||
"archive": "아카이브",
|
||||
"installer": "설치 프로그램",
|
||||
"code": "코드"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
"rename_folder": "폴더 이름 변경",
|
||||
"rename_file": "파일 이름 변경",
|
||||
"new_name": "새 이름",
|
||||
"new_folder_title": "새 폴더",
|
||||
"folder_name": "폴더 이름",
|
||||
"folder_placeholder": "내 폴더",
|
||||
"rename_title": "이름 변경",
|
||||
"move_file": "파일 이동",
|
||||
"move_folder": "폴더 이동",
|
||||
"select_destination": "대상 폴더를 선택하세요:",
|
||||
"select_this_folder": "이 폴더 선택",
|
||||
"go_to_parent": ".. (상위 폴더)",
|
||||
"no_subfolders": "하위 폴더 없음",
|
||||
"root": "루트",
|
||||
"delete_confirmation": "정말 삭제하시겠습니까",
|
||||
"and_contents": "및 모든 내용",
|
||||
"no_undo": "이 작업은 되돌릴 수 없습니다",
|
||||
"confirm_title": "작업 확인",
|
||||
"confirm_delete": "휴지통으로 이동",
|
||||
"confirm_delete_file": "파일 «{{name}}»을(를) 휴지통으로 이동하시겠습니까?",
|
||||
"confirm_delete_folder": "폴더 «{{name}}» 및 모든 내용을 휴지통으로 이동하시겠습니까?",
|
||||
"confirm_permanent_delete": "영구 삭제",
|
||||
"confirm_permanent_delete_msg": "이 항목을 영구적으로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
|
||||
"confirm_empty_trash": "휴지통 비우기",
|
||||
"confirm_delete_share": "공유 링크 삭제",
|
||||
"confirm_delete_share_msg": "이 공유 링크를 삭제하시겠습니까?",
|
||||
"share_file": "파일 공유",
|
||||
"existing_shares": "기존 공유",
|
||||
"share_options": "공유 옵션",
|
||||
"password": "비밀번호",
|
||||
"expiration": "만료일",
|
||||
"permissions": "권한",
|
||||
"generated_link": "생성된 링크",
|
||||
"notify": "알림 보내기",
|
||||
"recipient": "수신자",
|
||||
"message": "메시지"
|
||||
},
|
||||
"dropzone": {
|
||||
"drag_files": "여기에 파일을 드래그하거나 클릭하여 선택하세요",
|
||||
"drop_files": "파일을 놓아 업로드하세요"
|
||||
},
|
||||
"permissions": {
|
||||
"read": "읽기",
|
||||
"write": "쓰기",
|
||||
"reshare": "재공유"
|
||||
},
|
||||
"errors": {
|
||||
"file_not_found": "파일을 찾을 수 없습니다",
|
||||
"folder_not_found": "폴더를 찾을 수 없습니다",
|
||||
"delete_error": "삭제 오류",
|
||||
"upload_error": "파일 업로드 오류",
|
||||
"rename_error": "이름 변경 오류",
|
||||
"move_error": "이동 오류",
|
||||
"empty_name": "이름은 비워둘 수 없습니다",
|
||||
"name_exists": "같은 이름의 파일 또는 폴더가 이미 존재합니다",
|
||||
"generic_error": "오류가 발생했습니다"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"home": "홈"
|
||||
},
|
||||
"trash": {
|
||||
"empty_trash": "휴지통 비우기",
|
||||
"empty_state": "휴지통이 비어 있습니다",
|
||||
"original_location": "원래 위치",
|
||||
"deleted_date": "삭제일",
|
||||
"actions": "작업",
|
||||
"restore": "복원",
|
||||
"delete_permanently": "영구 삭제",
|
||||
"empty_confirm": "휴지통을 비우시겠습니까? 모든 항목이 영구적으로 삭제됩니다."
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "로그인",
|
||||
"username": "사용자 이름",
|
||||
"username_placeholder": "사용자 이름을 입력하세요",
|
||||
"password": "비밀번호",
|
||||
"password_placeholder": "비밀번호를 입력하세요",
|
||||
"login_button": "로그인",
|
||||
"no_account": "계정이 없으신가요?",
|
||||
"register": "가입하기",
|
||||
"admin_setup": "처음이신가요?",
|
||||
"setup": "관리자 설정",
|
||||
"register_title": "계정 만들기",
|
||||
"email": "이메일",
|
||||
"email_placeholder": "이메일 주소를 입력하세요",
|
||||
"confirm_password": "비밀번호 확인",
|
||||
"confirm_password_placeholder": "비밀번호를 다시 입력하세요",
|
||||
"register_button": "계정 만들기",
|
||||
"have_account": "이미 계정이 있으신가요?",
|
||||
"login": "로그인",
|
||||
"setup_title": "초기 설정",
|
||||
"setup_step1": "관리자",
|
||||
"setup_step2": "시스템",
|
||||
"setup_step3": "완료",
|
||||
"admin_username": "관리자 사용자 이름",
|
||||
"admin_email": "관리자 이메일",
|
||||
"admin_password": "관리자 비밀번호",
|
||||
"create_admin": "관리자 생성",
|
||||
"back_to_login": "이미 설정하셨나요?",
|
||||
"admin_success": "관리자 계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.",
|
||||
"account_success": "계정이 성공적으로 생성되었습니다! 로그인할 수 있습니다.",
|
||||
"passwords_mismatch": "비밀번호가 일치하지 않습니다",
|
||||
"admin_create_error": "관리자 계정 생성 오류",
|
||||
"or": "또는",
|
||||
"sso_login": "SSO로 로그인",
|
||||
"sso_login_provider": "{{provider}}(으)로 로그인"
|
||||
},
|
||||
"storage": {
|
||||
"title": "저장소",
|
||||
"calculating": "계산 중...",
|
||||
"used": "{{percentage}}% 사용 중 ({{used}} / {{total}})"
|
||||
},
|
||||
"viewer": {
|
||||
"unsupported_file": "이 파일 형식은 미리보기를 지원하지 않습니다.",
|
||||
"download_file": "파일 다운로드",
|
||||
"zoom_in": "확대",
|
||||
"zoom_out": "축소",
|
||||
"zoom_reset": "줌 초기화"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "환영합니다!",
|
||||
"subtitle": "계속하려면 언어를 선택하세요",
|
||||
"continue": "계속",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"zh": "中文",
|
||||
"fa": "فارسی",
|
||||
"fr": "Français",
|
||||
"de": "Deutsch",
|
||||
"pt": "Português",
|
||||
"ko": "한국어"
|
||||
}
|
||||
},
|
||||
"favorites": {
|
||||
"empty_state": "아직 즐겨찾기가 없습니다",
|
||||
"empty_hint": "파일이나 폴더에 별표를 눌러 즐겨찾기에 추가하세요",
|
||||
"add": "즐겨찾기 추가",
|
||||
"remove": "즐겨찾기 해제",
|
||||
"added_title": "즐겨찾기에 추가됨",
|
||||
"added_msg": "즐겨찾기에 추가되었습니다",
|
||||
"removed_title": "즐겨찾기에서 삭제됨",
|
||||
"removed_msg": "즐겨찾기에서 삭제되었습니다"
|
||||
},
|
||||
"recent": {
|
||||
"title": "최근",
|
||||
"clear": "최근 항목 지우기",
|
||||
"accessed": "접근일",
|
||||
"empty_state": "최근 파일이 없습니다",
|
||||
"empty_hint": "열어본 파일이 여기에 표시됩니다"
|
||||
},
|
||||
"notifications": {
|
||||
"file_renamed": "파일 이름이 변경되었습니다",
|
||||
"file_renamed_to": "파일 이름이 «{{name}}»(으)로 변경되었습니다",
|
||||
"folder_renamed": "폴더 이름이 변경되었습니다",
|
||||
"folder_renamed_to": "폴더 이름이 «{{name}}»(으)로 변경되었습니다",
|
||||
"file_uploaded": "파일이 업로드되었습니다",
|
||||
"file_deleted": "파일이 휴지통으로 이동되었습니다",
|
||||
"folder_deleted": "폴더가 휴지통으로 이동되었습니다",
|
||||
"item_deleted_permanently": "항목이 영구적으로 삭제되었습니다",
|
||||
"trash_emptied": "휴지통이 성공적으로 비워졌습니다",
|
||||
"title": "알림",
|
||||
"empty": "알림이 없습니다"
|
||||
},
|
||||
"batch": {
|
||||
"one_selected": "1개 선택됨",
|
||||
"n_selected": "{{count}}개 선택됨",
|
||||
"confirm_delete": "{{count}}개 항목을 휴지통으로 이동하시겠습니까?",
|
||||
"move_title": "{{count}}개 항목 이동",
|
||||
"add_favorites": "즐겨찾기에 추가",
|
||||
"move_copy": "이동 또는 복사"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "관리자 패널",
|
||||
"back_to_app": "OxiCloud로 돌아가기",
|
||||
"loading": "로딩 중…",
|
||||
"access_denied": "접근 거부",
|
||||
"access_denied_desc": "관리자 권한이 필요합니다.",
|
||||
"sign_in": "로그인",
|
||||
"tab_dashboard": "대시보드",
|
||||
"tab_users": "사용자",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "전체 사용자",
|
||||
"active_users": "활성 사용자",
|
||||
"admins": "관리자",
|
||||
"version": "버전",
|
||||
"storage_overview": "스토리지 개요",
|
||||
"used": "사용됨",
|
||||
"total_quota": "총 할당량",
|
||||
"usage_pct": "사용률",
|
||||
"users_over_80": "할당량 80% 초과",
|
||||
"users_over_quota": "할당량 초과",
|
||||
"system": "시스템",
|
||||
"auth_label": "인증",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "할당량",
|
||||
"enabled": "활성화됨",
|
||||
"disabled": "비활성화됨",
|
||||
"active": "활성",
|
||||
"off": "꺼짐",
|
||||
"allow_registration": "공개 자가 등록 허용",
|
||||
"registration_warning": "공개 등록이 비활성화되어 있습니다. 관리자만 사용자를 만들 수 있습니다.",
|
||||
"user_management": "사용자 관리",
|
||||
"create_user": "사용자 생성",
|
||||
"col_user": "사용자",
|
||||
"col_role": "역할",
|
||||
"col_auth": "인증",
|
||||
"col_status": "상태",
|
||||
"col_storage": "스토리지",
|
||||
"col_last_login": "마지막 로그인",
|
||||
"col_actions": "작업",
|
||||
"loading_users": "사용자 로딩 중…",
|
||||
"failed_load_users": "로드 실패",
|
||||
"no_users_found": "사용자 없음",
|
||||
"showing_users": "{{from}}-{{to}} / {{total}} 표시",
|
||||
"prev": "이전",
|
||||
"next": "다음",
|
||||
"inactive": "비활성",
|
||||
"you_badge": "(나)",
|
||||
"local": "로컬",
|
||||
"never": "없음",
|
||||
"just_now": "방금",
|
||||
"minutes_ago": "{{n}}분 전",
|
||||
"hours_ago": "{{n}}시간 전",
|
||||
"days_ago": "{{n}}일 전",
|
||||
"edit_quota_title": "할당량 편집",
|
||||
"reset_password_title": "비밀번호 재설정",
|
||||
"toggle_role_title": "역할 전환",
|
||||
"deactivate_title": "비활성화",
|
||||
"activate_title": "활성화",
|
||||
"delete_title": "삭제",
|
||||
"sso_title": "싱글 사인온 (OIDC / SSO)",
|
||||
"enable_sso": "SSO 인증 활성화",
|
||||
"provider_name": "제공자 이름",
|
||||
"issuer_url": "발급자 URL",
|
||||
"issuer_url_hint": "OpenID Connect 발급자 URL",
|
||||
"auto_discover": "자동 검색",
|
||||
"discovering": "검색 중…",
|
||||
"client_id": "클라이언트 ID",
|
||||
"client_secret": "클라이언트 시크릿",
|
||||
"client_secret_placeholder": "현재 값 유지하려면 비워두세요",
|
||||
"secret_configured": "클라이언트 시크릿 구성됨",
|
||||
"callback_url": "콜백 URL",
|
||||
"callback_url_hint": "(IdP에 등록)",
|
||||
"advanced_settings": "고급 설정",
|
||||
"scopes": "스코프",
|
||||
"auto_provision": "첫 로그인 시 자동 프로비저닝",
|
||||
"admin_groups": "관리자 그룹",
|
||||
"admin_groups_hint": "쉼표로 구분된 OIDC 그룹 이름",
|
||||
"disable_password": "비밀번호 로그인 비활성화 (OIDC만)",
|
||||
"password_warning": "모든 비밀번호 로그인이 차단됩니다!",
|
||||
"test_btn": "테스트",
|
||||
"save_btn": "저장",
|
||||
"saving": "저장 중…",
|
||||
"settings_saved": "설정 저장됨 — OIDC: {{status}}",
|
||||
"quota_modal_title": "스토리지 할당량 업데이트",
|
||||
"quota_user_label": "사용자:",
|
||||
"new_quota": "새 할당량",
|
||||
"quota_unlimited_hint": "무제한은 0",
|
||||
"cancel": "취소",
|
||||
"create_user_title": "새 사용자 생성",
|
||||
"username_label": "사용자 이름",
|
||||
"username_placeholder": "username",
|
||||
"username_hint": "3–32자",
|
||||
"password_label": "비밀번호",
|
||||
"password_placeholder": "최소 8자",
|
||||
"email_label": "이메일",
|
||||
"email_optional": "(선택사항)",
|
||||
"email_placeholder": "user@example.com (비어있으면 자동 생성)",
|
||||
"role_label": "역할",
|
||||
"role_user": "사용자",
|
||||
"role_admin": "관리자",
|
||||
"quota_label": "할당량",
|
||||
"creating": "생성 중…",
|
||||
"reset_pw_title": "비밀번호 재설정",
|
||||
"new_password_label": "새 비밀번호",
|
||||
"resetting": "재설정 중…",
|
||||
"reset_btn": "재설정",
|
||||
"confirm_role_change": "역할을 {{role}}(으)로 변경?",
|
||||
"confirm_deactivate": "이 사용자를 비활성화하시겠습니까?",
|
||||
"confirm_activate": "이 사용자를 활성화하시겠습니까?",
|
||||
"confirm_delete_user": "사용자 \"{{name}}\" 삭제? 되돌릴 수 없습니다!",
|
||||
"confirm_action": "작업 확인",
|
||||
"confirm_yes": "확인",
|
||||
"confirm_no": "취소",
|
||||
"error_username_short": "사용자 이름 최소 3자",
|
||||
"error_password_short": "비밀번호 최소 8자",
|
||||
"error_generic": "실패",
|
||||
"error_network": "네트워크 오류: {{message}}",
|
||||
"error_create_user": "사용자 생성 실패"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "프로필",
|
||||
"back_to_app": "OxiCloud로 돌아가기",
|
||||
"loading": "로딩 중…",
|
||||
"not_authenticated": "인증되지 않음",
|
||||
"not_authenticated_desc": "프로필을 보려면 로그인하세요.",
|
||||
"sign_in": "로그인",
|
||||
"role_admin": "관리자",
|
||||
"role_user": "사용자",
|
||||
"account_details": "계정 정보",
|
||||
"username": "사용자 이름",
|
||||
"email": "이메일",
|
||||
"role": "역할",
|
||||
"last_login": "마지막 로그인",
|
||||
"storage": "스토리지",
|
||||
"used": "사용됨",
|
||||
"quota": "할당량",
|
||||
"usage": "사용률",
|
||||
"unlimited": "무제한",
|
||||
"app_passwords": "앱 비밀번호",
|
||||
"app_pw_desc": "WebDAV, CalDAV, CardDAV 클라이언트용 비밀번호를 생성합니다. 각 비밀번호는 한 번만 표시됩니다.",
|
||||
"app_pw_label_placeholder": "라벨 (예: Thunderbird, macOS)",
|
||||
"generate": "생성",
|
||||
"generating": "생성 중…",
|
||||
"new_password_for": "새 비밀번호:",
|
||||
"copy_warning": "지금 이 비밀번호를 복사하세요. 다시 볼 수 없습니다.",
|
||||
"copy_to_clipboard": "클립보드에 복사",
|
||||
"col_label": "라벨",
|
||||
"col_created": "생성일",
|
||||
"col_last_used": "마지막 사용",
|
||||
"col_status": "상태",
|
||||
"active": "활성",
|
||||
"revoked": "취소됨",
|
||||
"revoke_title": "취소",
|
||||
"no_app_passwords": "앱 비밀번호가 아직 없습니다.",
|
||||
"client_sessions": "클라이언트 세션",
|
||||
"client_sessions_desc": "Nextcloud 호환 클라이언트 연결 시 자동 생성됩니다.",
|
||||
"col_client": "클라이언트",
|
||||
"never": "없음",
|
||||
"just_now": "방금",
|
||||
"minutes_ago": "{{n}}분 전",
|
||||
"hours_ago": "{{n}}시간 전",
|
||||
"days_ago": "{{n}}일 전",
|
||||
"change_password": "비밀번호 변경",
|
||||
"current_password": "현재 비밀번호",
|
||||
"new_password": "새 비밀번호",
|
||||
"min_8_chars": "최소 8자",
|
||||
"confirm_password": "새 비밀번호 확인",
|
||||
"update_password": "비밀번호 업데이트",
|
||||
"updating": "업데이트 중…",
|
||||
"password_updated": "비밀번호가 성공적으로 업데이트되었습니다",
|
||||
"passwords_no_match": "비밀번호가 일치하지 않습니다",
|
||||
"password_too_short": "비밀번호는 최소 8자여야 합니다",
|
||||
"password_change_failed": "비밀번호 변경 실패",
|
||||
"error_network": "네트워크 오류: {{message}}",
|
||||
"error_label_required": "라벨을 입력하세요",
|
||||
"error_create_pw": "앱 비밀번호 생성 실패",
|
||||
"confirm_revoke": "앱 비밀번호 \"{{label}}\"을(를) 취소하시겠습니까? 이 비밀번호를 사용하는 클라이언트가 작동하지 않게 됩니다.",
|
||||
"error_revoke": "취소 실패"
|
||||
}
|
||||
}
|
||||
+182
-2
@@ -182,6 +182,7 @@
|
||||
"size": "Grootte",
|
||||
"modified": "Gewijzigd",
|
||||
"no_files": "Geen bestanden in deze map",
|
||||
"empty_hint": "Upload bestanden of maak mappen aan om te beginnen",
|
||||
"loading": "Bestanden laden…",
|
||||
"view_grid": "Rasterweergave",
|
||||
"view_list": "Lijstweergave",
|
||||
@@ -317,8 +318,8 @@
|
||||
"zoom_reset": "Zoom terugzetten"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "Welkom bij OxiCloud",
|
||||
"subtitle": "Selecteer je taal",
|
||||
"title": "Welkom!",
|
||||
"subtitle": "Selecteer je taal om door te gaan",
|
||||
"continue": "Doorgaan",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -367,5 +368,184 @@
|
||||
"move_title": "Verplaats {count} item(s)",
|
||||
"add_favorites": "Toevoegen aan favorieten",
|
||||
"move_copy": "Verplaatsen of kopiëren"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "Beheerderspaneel",
|
||||
"back_to_app": "Terug naar OxiCloud",
|
||||
"loading": "Laden…",
|
||||
"access_denied": "Toegang geweigerd",
|
||||
"access_denied_desc": "Beheerdersrechten vereist.",
|
||||
"sign_in": "Inloggen",
|
||||
"tab_dashboard": "Dashboard",
|
||||
"tab_users": "Gebruikers",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "Totaal gebruikers",
|
||||
"active_users": "Actieve gebruikers",
|
||||
"admins": "Beheerders",
|
||||
"version": "Versie",
|
||||
"storage_overview": "Opslagoverzicht",
|
||||
"used": "Gebruikt",
|
||||
"total_quota": "Totaal quotum",
|
||||
"usage_pct": "Gebruik %",
|
||||
"users_over_80": "Gebruikers >80% quotum",
|
||||
"users_over_quota": "Gebruikers boven quotum",
|
||||
"system": "Systeem",
|
||||
"auth_label": "Auth",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "Quota",
|
||||
"enabled": "Ingeschakeld",
|
||||
"disabled": "Uitgeschakeld",
|
||||
"active": "Actief",
|
||||
"off": "Uit",
|
||||
"allow_registration": "Openbare zelfregistratie toestaan",
|
||||
"registration_warning": "Openbare registratie is uitgeschakeld. Alleen beheerders kunnen gebruikers aanmaken.",
|
||||
"user_management": "Gebruikersbeheer",
|
||||
"create_user": "Gebruiker aanmaken",
|
||||
"col_user": "Gebruiker",
|
||||
"col_role": "Rol",
|
||||
"col_auth": "Auth",
|
||||
"col_status": "Status",
|
||||
"col_storage": "Opslag",
|
||||
"col_last_login": "Laatste login",
|
||||
"col_actions": "Acties",
|
||||
"loading_users": "Gebruikers laden…",
|
||||
"failed_load_users": "Laden mislukt",
|
||||
"no_users_found": "Geen gebruikers gevonden",
|
||||
"showing_users": "Toont {{from}}-{{to}} van {{total}}",
|
||||
"prev": "Vorige",
|
||||
"next": "Volgende",
|
||||
"inactive": "Inactief",
|
||||
"you_badge": "(jij)",
|
||||
"local": "Lokaal",
|
||||
"never": "Nooit",
|
||||
"just_now": "Zojuist",
|
||||
"minutes_ago": "{{n}}min geleden",
|
||||
"hours_ago": "{{n}}u geleden",
|
||||
"days_ago": "{{n}}d geleden",
|
||||
"edit_quota_title": "Quotum bewerken",
|
||||
"reset_password_title": "Wachtwoord resetten",
|
||||
"toggle_role_title": "Rol wisselen",
|
||||
"deactivate_title": "Deactiveren",
|
||||
"activate_title": "Activeren",
|
||||
"delete_title": "Verwijderen",
|
||||
"sso_title": "Single Sign-On (OIDC / SSO)",
|
||||
"enable_sso": "SSO-authenticatie inschakelen",
|
||||
"provider_name": "Providernaam",
|
||||
"issuer_url": "Uitgever-URL",
|
||||
"issuer_url_hint": "OpenID Connect uitgever-URL",
|
||||
"auto_discover": "Auto-ontdekking",
|
||||
"discovering": "Ontdekken…",
|
||||
"client_id": "Client-ID",
|
||||
"client_secret": "Client-secret",
|
||||
"client_secret_placeholder": "Laat leeg om huidige waarde te behouden",
|
||||
"secret_configured": "Een client-secret is al geconfigureerd",
|
||||
"callback_url": "Callback-URL",
|
||||
"callback_url_hint": "(registreer bij uw IdP)",
|
||||
"advanced_settings": "Geavanceerde instellingen",
|
||||
"scopes": "Scopes",
|
||||
"auto_provision": "Gebruikers automatisch aanmaken bij eerste login",
|
||||
"admin_groups": "Beheergroepen",
|
||||
"admin_groups_hint": "Kommagescheiden OIDC-groepsnamen",
|
||||
"disable_password": "Wachtwoord-login uitschakelen (alleen OIDC)",
|
||||
"password_warning": "Dit voorkomt ALLE logins op basis van wachtwoord!",
|
||||
"test_btn": "Testen",
|
||||
"save_btn": "Opslaan",
|
||||
"saving": "Opslaan…",
|
||||
"settings_saved": "Instellingen opgeslagen — OIDC is nu {{status}}",
|
||||
"quota_modal_title": "Opslagquotum bijwerken",
|
||||
"quota_user_label": "Gebruiker:",
|
||||
"new_quota": "Nieuw quotum",
|
||||
"quota_unlimited_hint": "0 voor onbeperkt",
|
||||
"cancel": "Annuleren",
|
||||
"create_user_title": "Nieuwe gebruiker aanmaken",
|
||||
"username_label": "Gebruikersnaam",
|
||||
"username_placeholder": "jandevries",
|
||||
"username_hint": "3–32 tekens",
|
||||
"password_label": "Wachtwoord",
|
||||
"password_placeholder": "Min 8 tekens",
|
||||
"email_label": "E-mail",
|
||||
"email_optional": "(optioneel)",
|
||||
"email_placeholder": "gebruiker@voorbeeld.nl (automatisch indien leeg)",
|
||||
"role_label": "Rol",
|
||||
"role_user": "Gebruiker",
|
||||
"role_admin": "Beheerder",
|
||||
"quota_label": "Quotum",
|
||||
"creating": "Aanmaken…",
|
||||
"reset_pw_title": "Wachtwoord resetten",
|
||||
"new_password_label": "Nieuw wachtwoord",
|
||||
"resetting": "Resetten…",
|
||||
"reset_btn": "Resetten",
|
||||
"confirm_role_change": "Rol wijzigen naar {{role}}?",
|
||||
"confirm_deactivate": "Weet u zeker dat u deze gebruiker wilt deactiveren?",
|
||||
"confirm_activate": "Weet u zeker dat u deze gebruiker wilt activeren?",
|
||||
"confirm_delete_user": "Gebruiker \"{{name}}\" VERWIJDEREN? Kan niet ongedaan worden gemaakt!",
|
||||
"confirm_action": "Actie bevestigen",
|
||||
"confirm_yes": "Bevestigen",
|
||||
"confirm_no": "Annuleren",
|
||||
"error_username_short": "Gebruikersnaam moet minimaal 3 tekens bevatten",
|
||||
"error_password_short": "Wachtwoord moet minimaal 8 tekens bevatten",
|
||||
"error_generic": "Mislukt",
|
||||
"error_network": "Netwerkfout: {{message}}",
|
||||
"error_create_user": "Kan gebruiker niet aanmaken"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "Profiel",
|
||||
"back_to_app": "Terug naar OxiCloud",
|
||||
"loading": "Laden…",
|
||||
"not_authenticated": "Niet geauthenticeerd",
|
||||
"not_authenticated_desc": "Log in om uw profiel te bekijken.",
|
||||
"sign_in": "Inloggen",
|
||||
"role_admin": "Beheerder",
|
||||
"role_user": "Gebruiker",
|
||||
"account_details": "Accountgegevens",
|
||||
"username": "Gebruikersnaam",
|
||||
"email": "E-mail",
|
||||
"role": "Rol",
|
||||
"last_login": "Laatste login",
|
||||
"storage": "Opslag",
|
||||
"used": "Gebruikt",
|
||||
"quota": "Quotum",
|
||||
"usage": "Gebruik",
|
||||
"unlimited": "Onbeperkt",
|
||||
"app_passwords": "App-wachtwoorden",
|
||||
"app_pw_desc": "Genereer wachtwoorden voor WebDAV-, CalDAV- en CardDAV-clients. Elk wachtwoord wordt slechts één keer getoond.",
|
||||
"app_pw_label_placeholder": "Label (bijv. Thunderbird, macOS)",
|
||||
"generate": "Genereren",
|
||||
"generating": "Genereren…",
|
||||
"new_password_for": "Nieuw wachtwoord voor",
|
||||
"copy_warning": "Kopieer dit wachtwoord nu. U kunt het niet opnieuw bekijken.",
|
||||
"copy_to_clipboard": "Kopiëren naar klembord",
|
||||
"col_label": "Label",
|
||||
"col_created": "Aangemaakt",
|
||||
"col_last_used": "Laatst gebruikt",
|
||||
"col_status": "Status",
|
||||
"active": "Actief",
|
||||
"revoked": "Ingetrokken",
|
||||
"revoke_title": "Intrekken",
|
||||
"no_app_passwords": "Nog geen app-wachtwoorden.",
|
||||
"client_sessions": "Clientsessies",
|
||||
"client_sessions_desc": "Automatisch gegenereerd bij het verbinden van een Nextcloud-compatibele client.",
|
||||
"col_client": "Client",
|
||||
"never": "Nooit",
|
||||
"just_now": "Zojuist",
|
||||
"minutes_ago": "{{n}} min geleden",
|
||||
"hours_ago": "{{n}}u geleden",
|
||||
"days_ago": "{{n}} dagen geleden",
|
||||
"change_password": "Wachtwoord wijzigen",
|
||||
"current_password": "Huidig wachtwoord",
|
||||
"new_password": "Nieuw wachtwoord",
|
||||
"min_8_chars": "Minimaal 8 tekens",
|
||||
"confirm_password": "Bevestig nieuw wachtwoord",
|
||||
"update_password": "Wachtwoord bijwerken",
|
||||
"updating": "Bijwerken…",
|
||||
"password_updated": "Wachtwoord succesvol bijgewerkt",
|
||||
"passwords_no_match": "Wachtwoorden komen niet overeen",
|
||||
"password_too_short": "Wachtwoord moet minimaal 8 tekens bevatten",
|
||||
"password_change_failed": "Wachtwoord wijzigen mislukt",
|
||||
"error_network": "Netwerkfout: {{message}}",
|
||||
"error_label_required": "Voer een label in",
|
||||
"error_create_pw": "App-wachtwoord aanmaken mislukt",
|
||||
"confirm_revoke": "App-wachtwoord \"{{label}}\" intrekken? Clients die dit wachtwoord gebruiken zullen stoppen.",
|
||||
"error_revoke": "Intrekken mislukt"
|
||||
}
|
||||
}
|
||||
|
||||
+182
-2
@@ -182,6 +182,7 @@
|
||||
"size": "Tamanho",
|
||||
"modified": "Modificado",
|
||||
"no_files": "Nenhum arquivo nesta pasta",
|
||||
"empty_hint": "Envie arquivos ou crie pastas para começar",
|
||||
"loading": "Carregando arquivos…",
|
||||
"view_grid": "Visualização em grade",
|
||||
"view_list": "Visualização em lista",
|
||||
@@ -317,8 +318,8 @@
|
||||
"zoom_reset": "Redefinir zoom"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "Bem-vindo ao OxiCloud",
|
||||
"subtitle": "Por favor, selecione seu idioma",
|
||||
"title": "Bem-vindo!",
|
||||
"subtitle": "Selecione seu idioma para continuar",
|
||||
"continue": "Continuar",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -365,5 +366,184 @@
|
||||
"move_title": "Mover {{count}} item(ns)",
|
||||
"add_favorites": "Adicionar aos favoritos",
|
||||
"move_copy": "Mover ou copiar"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "Painel de Administração",
|
||||
"back_to_app": "Voltar ao OxiCloud",
|
||||
"loading": "Carregando…",
|
||||
"access_denied": "Acesso Negado",
|
||||
"access_denied_desc": "Privilégios de administrador necessários.",
|
||||
"sign_in": "Entrar",
|
||||
"tab_dashboard": "Painel",
|
||||
"tab_users": "Usuários",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "Total de Usuários",
|
||||
"active_users": "Usuários Ativos",
|
||||
"admins": "Admins",
|
||||
"version": "Versão",
|
||||
"storage_overview": "Visão do Armazenamento",
|
||||
"used": "Usado",
|
||||
"total_quota": "Cota Total",
|
||||
"usage_pct": "Uso %",
|
||||
"users_over_80": "Usuários >80% cota",
|
||||
"users_over_quota": "Usuários acima da cota",
|
||||
"system": "Sistema",
|
||||
"auth_label": "Auth",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "Cotas",
|
||||
"enabled": "Habilitado",
|
||||
"disabled": "Desabilitado",
|
||||
"active": "Ativo",
|
||||
"off": "Inativo",
|
||||
"allow_registration": "Permitir registro público",
|
||||
"registration_warning": "O registro público está desabilitado. Apenas administradores podem criar novos usuários.",
|
||||
"user_management": "Gerenciamento de Usuários",
|
||||
"create_user": "Criar Usuário",
|
||||
"col_user": "Usuário",
|
||||
"col_role": "Função",
|
||||
"col_auth": "Auth",
|
||||
"col_status": "Status",
|
||||
"col_storage": "Armazenamento",
|
||||
"col_last_login": "Último Login",
|
||||
"col_actions": "Ações",
|
||||
"loading_users": "Carregando usuários…",
|
||||
"failed_load_users": "Falha ao carregar",
|
||||
"no_users_found": "Nenhum usuário encontrado",
|
||||
"showing_users": "Mostrando {{from}}-{{to}} de {{total}}",
|
||||
"prev": "Anterior",
|
||||
"next": "Próximo",
|
||||
"inactive": "Inativo",
|
||||
"you_badge": "(você)",
|
||||
"local": "Local",
|
||||
"never": "Nunca",
|
||||
"just_now": "Agora mesmo",
|
||||
"minutes_ago": "{{n}}min atrás",
|
||||
"hours_ago": "{{n}}h atrás",
|
||||
"days_ago": "{{n}}d atrás",
|
||||
"edit_quota_title": "Editar cota",
|
||||
"reset_password_title": "Redefinir senha",
|
||||
"toggle_role_title": "Alternar função",
|
||||
"deactivate_title": "Desativar",
|
||||
"activate_title": "Ativar",
|
||||
"delete_title": "Excluir",
|
||||
"sso_title": "Login Único (OIDC / SSO)",
|
||||
"enable_sso": "Habilitar autenticação SSO",
|
||||
"provider_name": "Nome do Provedor",
|
||||
"issuer_url": "URL do Emissor",
|
||||
"issuer_url_hint": "URL do emissor OpenID Connect",
|
||||
"auto_discover": "Auto-descoberta",
|
||||
"discovering": "Descobrindo…",
|
||||
"client_id": "Client ID",
|
||||
"client_secret": "Client Secret",
|
||||
"client_secret_placeholder": "Deixe vazio para manter o valor atual",
|
||||
"secret_configured": "Um client secret já está configurado",
|
||||
"callback_url": "URL de Callback",
|
||||
"callback_url_hint": "(registrar no seu IdP)",
|
||||
"advanced_settings": "Configurações Avançadas",
|
||||
"scopes": "Scopes",
|
||||
"auto_provision": "Provisionar usuários automaticamente",
|
||||
"admin_groups": "Grupos de Admin",
|
||||
"admin_groups_hint": "Nomes de grupos OIDC separados por vírgula",
|
||||
"disable_password": "Desabilitar login por senha (apenas OIDC)",
|
||||
"password_warning": "Isso impedirá TODOS os logins por senha!",
|
||||
"test_btn": "Testar",
|
||||
"save_btn": "Salvar",
|
||||
"saving": "Salvando…",
|
||||
"settings_saved": "Configurações salvas — OIDC agora está {{status}}",
|
||||
"quota_modal_title": "Atualizar Cota",
|
||||
"quota_user_label": "Usuário:",
|
||||
"new_quota": "Nova Cota",
|
||||
"quota_unlimited_hint": "0 para ilimitado",
|
||||
"cancel": "Cancelar",
|
||||
"create_user_title": "Criar Novo Usuário",
|
||||
"username_label": "Nome de usuário",
|
||||
"username_placeholder": "joaosilva",
|
||||
"username_hint": "3–32 caracteres",
|
||||
"password_label": "Senha",
|
||||
"password_placeholder": "Mín 8 caracteres",
|
||||
"email_label": "E-mail",
|
||||
"email_optional": "(opcional)",
|
||||
"email_placeholder": "usuario@exemplo.com (gerado automaticamente se vazio)",
|
||||
"role_label": "Função",
|
||||
"role_user": "Usuário",
|
||||
"role_admin": "Admin",
|
||||
"quota_label": "Cota",
|
||||
"creating": "Criando…",
|
||||
"reset_pw_title": "Redefinir Senha",
|
||||
"new_password_label": "Nova Senha",
|
||||
"resetting": "Redefinindo…",
|
||||
"reset_btn": "Redefinir",
|
||||
"confirm_role_change": "Alterar função para {{role}}?",
|
||||
"confirm_deactivate": "Tem certeza de que deseja desativar este usuário?",
|
||||
"confirm_activate": "Tem certeza de que deseja ativar este usuário?",
|
||||
"confirm_delete_user": "EXCLUIR usuário \"{{name}}\"? Não pode ser desfeito!",
|
||||
"confirm_action": "Confirmar Ação",
|
||||
"confirm_yes": "Confirmar",
|
||||
"confirm_no": "Cancelar",
|
||||
"error_username_short": "O nome de usuário deve ter pelo menos 3 caracteres",
|
||||
"error_password_short": "A senha deve ter pelo menos 8 caracteres",
|
||||
"error_generic": "Falha",
|
||||
"error_network": "Erro de rede: {{message}}",
|
||||
"error_create_user": "Falha ao criar usuário"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "Perfil",
|
||||
"back_to_app": "Voltar ao OxiCloud",
|
||||
"loading": "Carregando…",
|
||||
"not_authenticated": "Não Autenticado",
|
||||
"not_authenticated_desc": "Faça login para ver seu perfil.",
|
||||
"sign_in": "Entrar",
|
||||
"role_admin": "Administrador",
|
||||
"role_user": "Usuário",
|
||||
"account_details": "Detalhes da Conta",
|
||||
"username": "Nome de usuário",
|
||||
"email": "E-mail",
|
||||
"role": "Função",
|
||||
"last_login": "Último login",
|
||||
"storage": "Armazenamento",
|
||||
"used": "Usado",
|
||||
"quota": "Cota",
|
||||
"usage": "Uso",
|
||||
"unlimited": "Ilimitado",
|
||||
"app_passwords": "Senhas de Aplicativo",
|
||||
"app_pw_desc": "Gere senhas para clientes WebDAV, CalDAV e CardDAV. Cada senha é exibida apenas uma vez.",
|
||||
"app_pw_label_placeholder": "Rótulo (ex. Thunderbird, macOS)",
|
||||
"generate": "Gerar",
|
||||
"generating": "Gerando…",
|
||||
"new_password_for": "Nova senha para",
|
||||
"copy_warning": "Copie esta senha agora. Você não poderá vê-la novamente.",
|
||||
"copy_to_clipboard": "Copiar para área de transferência",
|
||||
"col_label": "Rótulo",
|
||||
"col_created": "Criado",
|
||||
"col_last_used": "Último uso",
|
||||
"col_status": "Status",
|
||||
"active": "Ativa",
|
||||
"revoked": "Revogada",
|
||||
"revoke_title": "Revogar",
|
||||
"no_app_passwords": "Nenhuma senha de aplicativo ainda.",
|
||||
"client_sessions": "Sessões de cliente",
|
||||
"client_sessions_desc": "Geradas automaticamente ao conectar um cliente compatível com Nextcloud.",
|
||||
"col_client": "Cliente",
|
||||
"never": "Nunca",
|
||||
"just_now": "Agora mesmo",
|
||||
"minutes_ago": "{{n}} min atrás",
|
||||
"hours_ago": "{{n}}h atrás",
|
||||
"days_ago": "{{n}} dias atrás",
|
||||
"change_password": "Alterar Senha",
|
||||
"current_password": "Senha Atual",
|
||||
"new_password": "Nova Senha",
|
||||
"min_8_chars": "Pelo menos 8 caracteres",
|
||||
"confirm_password": "Confirmar Nova Senha",
|
||||
"update_password": "Atualizar Senha",
|
||||
"updating": "Atualizando…",
|
||||
"password_updated": "Senha atualizada com sucesso",
|
||||
"passwords_no_match": "As senhas não coincidem",
|
||||
"password_too_short": "A senha deve ter pelo menos 8 caracteres",
|
||||
"password_change_failed": "Falha ao alterar a senha",
|
||||
"error_network": "Erro de rede: {{message}}",
|
||||
"error_label_required": "Digite um rótulo",
|
||||
"error_create_pw": "Falha ao criar senha de aplicativo",
|
||||
"confirm_revoke": "Revogar senha \"{{label}}\"? Clientes que usam esta senha deixarão de funcionar.",
|
||||
"error_revoke": "Falha ao revogar"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
{
|
||||
"app": {
|
||||
"title": "OxiCloud",
|
||||
"description": "Минималистичная система облачного хранения"
|
||||
},
|
||||
"nav": {
|
||||
"files": "Файлы",
|
||||
"shared": "Общие",
|
||||
"recent": "Недавние",
|
||||
"favorites": "Избранное",
|
||||
"photos": "Фото",
|
||||
"trash": "Корзина"
|
||||
},
|
||||
"photos": {
|
||||
"empty_state": "Фотографий пока нет",
|
||||
"empty_hint": "Загрузите изображения или видео, чтобы увидеть их здесь",
|
||||
"items_selected": "выбрано",
|
||||
"view_daily": "День",
|
||||
"view_monthly": "Месяц",
|
||||
"view_yearly": "Год"
|
||||
},
|
||||
"actions": {
|
||||
"search": "Поиск файлов...",
|
||||
"new_folder": "Новая папка",
|
||||
"upload": "Загрузить",
|
||||
"upload_files": "Загрузить файлы",
|
||||
"upload_folder": "Загрузить папку",
|
||||
"upload.uploading": "Загрузка...",
|
||||
"upload.complete": "{count} / {total} загружено",
|
||||
"rename": "Переименовать",
|
||||
"move": "Переместить в...",
|
||||
"move_to": "Переместить в",
|
||||
"delete": "Удалить",
|
||||
"download": "Скачать",
|
||||
"view": "Просмотр",
|
||||
"cancel": "Отмена",
|
||||
"confirm": "Подтвердить",
|
||||
"share": "Поделиться",
|
||||
"favorite": "В избранное",
|
||||
"unfavorite": "Из избранного",
|
||||
"copy": "Копировать",
|
||||
"notify": "Уведомить",
|
||||
"send": "Отправить",
|
||||
"clear_recent": "Очистить недавние",
|
||||
"logout": "Выйти",
|
||||
"create": "Создать",
|
||||
"search_btn": "Найти",
|
||||
"close": "Закрыть",
|
||||
"delete_permanently": "Удалить навсегда",
|
||||
"empty_trash": "Очистить корзину"
|
||||
},
|
||||
"user_menu": {
|
||||
"appearance": "Оформление",
|
||||
"about": "О OxiCloud",
|
||||
"about_description": "Платформа облачного хранения на Rust с чистой архитектурой. Быстрая, безопасная и конфиденциальная.",
|
||||
"admin_panel": "Панель администратора",
|
||||
"profile": "Мой профиль",
|
||||
"role_user": "Пользователь"
|
||||
},
|
||||
"share": {
|
||||
"dialogTitle": "Ссылка для обмена",
|
||||
"linkLabel": "Ссылка:",
|
||||
"copyLink": "Копировать",
|
||||
"permissions": "Разрешения:",
|
||||
"permissionRead": "Чтение",
|
||||
"permissionWrite": "Запись",
|
||||
"permissionReshare": "Пересылка",
|
||||
"password": "Защита паролем:",
|
||||
"generatePassword": "Сгенерировать",
|
||||
"expiration": "Срок действия:",
|
||||
"update": "Обновить общий доступ",
|
||||
"remove": "Удалить общий доступ",
|
||||
"notifyTitle": "Отправить уведомление",
|
||||
"notifyEmailLabel": "Адрес email:",
|
||||
"notifyMessageLabel": "Сообщение (необязательно):",
|
||||
"notifySend": "Отправить уведомление",
|
||||
"shareWithOthers": "Поделиться с другими",
|
||||
"sharePublicly": "Общий доступ",
|
||||
"shareSettings": "Настройки общего доступа",
|
||||
"shareCopied": "Ссылка скопирована в буфер обмена",
|
||||
"shareCreated": "Ссылка для общего доступа успешно создана",
|
||||
"shareUpdated": "Настройки общего доступа успешно обновлены",
|
||||
"shareRemoved": "Общий доступ успешно удалён"
|
||||
},
|
||||
"share_dialogTitle": "Ссылка для обмена",
|
||||
"share_linkLabel": "Ссылка:",
|
||||
"share_copyLink": "Копировать",
|
||||
"share_permissions": "Разрешения:",
|
||||
"share_permissionRead": "Чтение",
|
||||
"share_permissionWrite": "Запись",
|
||||
"share_permissionReshare": "Пересылка",
|
||||
"share_password": "Защита паролем:",
|
||||
"share_generatePassword": "Сгенерировать",
|
||||
"share_expiration": "Срок действия:",
|
||||
"share_update": "Обновить общий доступ",
|
||||
"share_remove": "Удалить общий доступ",
|
||||
"share_notifyTitle": "Отправить уведомление",
|
||||
"share_notifyEmailLabel": "Адрес email:",
|
||||
"share_notifyMessageLabel": "Сообщение (необязательно):",
|
||||
"share_notifySend": "Отправить уведомление",
|
||||
"shared": {
|
||||
"backToFiles": "Назад к файлам",
|
||||
"pageTitle": "Общие ресурсы",
|
||||
"pageDescription": "Управление общими файлами и папками",
|
||||
"filterType": "Тип:",
|
||||
"filterAll": "Все",
|
||||
"filterFiles": "Файлы",
|
||||
"filterFolders": "Папки",
|
||||
"sortBy": "Сортировка:",
|
||||
"sortByName": "Имя",
|
||||
"sortByDate": "Дата",
|
||||
"sortByExpiration": "Срок действия",
|
||||
"search": "Поиск",
|
||||
"colName": "Имя",
|
||||
"colType": "Тип",
|
||||
"colDateShared": "Дата общего доступа",
|
||||
"colExpiration": "Срок действия",
|
||||
"colPermissions": "Разрешения",
|
||||
"colPassword": "Пароль",
|
||||
"colActions": "Действия",
|
||||
"emptyStateTitle": "Общих ресурсов пока нет",
|
||||
"emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь",
|
||||
"goToFiles": "Перейти к файлам",
|
||||
"typeFile": "Файл",
|
||||
"typeFolder": "Папка",
|
||||
"noExpiration": "Без срока",
|
||||
"hasPassword": "Да",
|
||||
"noPassword": "Нет",
|
||||
"editShare": "Изменить общий доступ",
|
||||
"notifyShare": "Уведомить",
|
||||
"copyLink": "Копировать ссылку",
|
||||
"removeShare": "Удалить общий доступ",
|
||||
"linkCopied": "Ссылка скопирована в буфер обмена!",
|
||||
"linkCopyFailed": "Не удалось скопировать ссылку",
|
||||
"itemUpdated": "Настройки общего доступа обновлены",
|
||||
"itemRemoved": "Общий доступ удалён",
|
||||
"invalidEmail": "Укажите корректный адрес email",
|
||||
"notificationSent": "Уведомление успешно отправлено",
|
||||
"notificationFailed": "Не удалось отправить уведомление",
|
||||
"shared_backToFiles": "Назад к файлам",
|
||||
"shared_pageTitle": "Общие ресурсы",
|
||||
"shared_pageDescription": "Управление общими файлами и папками",
|
||||
"shared_filterType": "Тип:",
|
||||
"shared_filterAll": "Все",
|
||||
"shared_filterFiles": "Файлы",
|
||||
"shared_filterFolders": "Папки",
|
||||
"shared_sortBy": "Сортировка:",
|
||||
"shared_sortByName": "Имя",
|
||||
"shared_sortByDate": "Дата",
|
||||
"shared_sortByExpiration": "Срок действия",
|
||||
"shared_search": "Поиск",
|
||||
"shared_colName": "Имя",
|
||||
"shared_colType": "Тип",
|
||||
"shared_colDateShared": "Дата общего доступа",
|
||||
"shared_colExpiration": "Срок действия",
|
||||
"shared_colPermissions": "Разрешения",
|
||||
"shared_colPassword": "Пароль",
|
||||
"shared_colActions": "Действия",
|
||||
"shared_emptyStateTitle": "Общих ресурсов пока нет",
|
||||
"shared_emptyStateDesc": "Когда вы поделитесь файлами или папками, они появятся здесь",
|
||||
"shared_goToFiles": "Перейти к файлам",
|
||||
"shared_typeFile": "Файл",
|
||||
"shared_typeFolder": "Папка",
|
||||
"shared_noExpiration": "Без срока",
|
||||
"shared_hasPassword": "Да",
|
||||
"shared_noPassword": "Нет",
|
||||
"shared_editShare": "Изменить общий доступ",
|
||||
"shared_notifyShare": "Уведомить",
|
||||
"shared_copyLink": "Копировать ссылку",
|
||||
"shared_removeShare": "Удалить общий доступ",
|
||||
"shared_linkCopied": "Ссылка скопирована в буфер обмена!",
|
||||
"shared_linkCopyFailed": "Не удалось скопировать ссылку",
|
||||
"shared_itemUpdated": "Настройки общего доступа обновлены",
|
||||
"shared_itemRemoved": "Общий доступ удалён",
|
||||
"shared_invalidEmail": "Укажите корректный адрес email",
|
||||
"shared_notificationSent": "Уведомление успешно отправлено",
|
||||
"shared_notificationFailed": "Не удалось отправить уведомление"
|
||||
},
|
||||
"files": {
|
||||
"name": "Имя",
|
||||
"type": "Тип",
|
||||
"size": "Размер",
|
||||
"modified": "Изменён",
|
||||
"no_files": "В этой папке нет файлов",
|
||||
"empty_hint": "Загрузите файлы или создайте папки, чтобы начать",
|
||||
"loading": "Загрузка файлов…",
|
||||
"view_grid": "Сетка",
|
||||
"view_list": "Список",
|
||||
"file_types": {
|
||||
"document": "Документ",
|
||||
"image": "Изображение",
|
||||
"video": "Видео",
|
||||
"audio": "Аудио",
|
||||
"pdf": "PDF",
|
||||
"text": "Текст",
|
||||
"folder": "Папка",
|
||||
"spreadsheet": "Таблица",
|
||||
"presentation": "Презентация",
|
||||
"archive": "Архив",
|
||||
"installer": "Установщик",
|
||||
"code": "Код"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
"rename_folder": "Переименовать папку",
|
||||
"rename_file": "Переименовать файл",
|
||||
"new_name": "Новое имя",
|
||||
"new_folder_title": "Новая папка",
|
||||
"folder_name": "Имя папки",
|
||||
"folder_placeholder": "Моя папка",
|
||||
"rename_title": "Переименовать",
|
||||
"move_file": "Переместить файл",
|
||||
"move_folder": "Переместить папку",
|
||||
"select_destination": "Выберите папку назначения:",
|
||||
"select_this_folder": "Выбрать эту папку",
|
||||
"go_to_parent": ".. (родительская папка)",
|
||||
"no_subfolders": "Нет подпапок",
|
||||
"root": "Корень",
|
||||
"delete_confirmation": "Вы уверены, что хотите удалить",
|
||||
"and_contents": "и всё его содержимое",
|
||||
"no_undo": "Это действие невозможно отменить",
|
||||
"confirm_title": "Подтверждение действия",
|
||||
"confirm_delete": "В корзину",
|
||||
"confirm_delete_file": "Вы уверены, что хотите переместить файл \"{{name}}\" в корзину?",
|
||||
"confirm_delete_folder": "Вы уверены, что хотите переместить папку \"{{name}}\" и всё её содержимое в корзину?",
|
||||
"confirm_permanent_delete": "Удалить навсегда",
|
||||
"confirm_permanent_delete_msg": "Вы уверены, что хотите навсегда удалить этот элемент? Это действие невозможно отменить.",
|
||||
"confirm_empty_trash": "Очистить корзину",
|
||||
"confirm_delete_share": "Удалить ссылку общего доступа",
|
||||
"confirm_delete_share_msg": "Вы уверены, что хотите удалить эту ссылку общего доступа?",
|
||||
"share_file": "Поделиться файлом",
|
||||
"existing_shares": "Существующие общие доступы",
|
||||
"share_options": "Параметры общего доступа",
|
||||
"password": "Пароль",
|
||||
"expiration": "Срок действия",
|
||||
"permissions": "Разрешения",
|
||||
"generated_link": "Сгенерированная ссылка",
|
||||
"notify": "Отправить уведомление",
|
||||
"recipient": "Получатель",
|
||||
"message": "Сообщение"
|
||||
},
|
||||
"dropzone": {
|
||||
"drag_files": "Перетащите файлы сюда или нажмите для выбора",
|
||||
"drop_files": "Отпустите файлы для загрузки"
|
||||
},
|
||||
"permissions": {
|
||||
"read": "Чтение",
|
||||
"write": "Запись",
|
||||
"reshare": "Пересылка"
|
||||
},
|
||||
"errors": {
|
||||
"file_not_found": "Файл не найден",
|
||||
"folder_not_found": "Папка не найдена",
|
||||
"delete_error": "Ошибка удаления",
|
||||
"upload_error": "Ошибка загрузки файла",
|
||||
"rename_error": "Ошибка переименования",
|
||||
"move_error": "Ошибка перемещения",
|
||||
"empty_name": "Имя не может быть пустым",
|
||||
"name_exists": "Файл или папка с таким именем уже существует",
|
||||
"generic_error": "Произошла ошибка"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"home": "Главная"
|
||||
},
|
||||
"trash": {
|
||||
"empty_trash": "Очистить корзину",
|
||||
"empty_state": "Корзина пуста",
|
||||
"original_location": "Исходное расположение",
|
||||
"deleted_date": "Дата удаления",
|
||||
"actions": "Действия",
|
||||
"restore": "Восстановить",
|
||||
"delete_permanently": "Удалить навсегда",
|
||||
"empty_confirm": "Вы уверены, что хотите очистить корзину? Все элементы будут удалены навсегда."
|
||||
},
|
||||
"auth": {
|
||||
"login_title": "Вход",
|
||||
"username": "Имя пользователя",
|
||||
"username_placeholder": "Введите имя пользователя",
|
||||
"password": "Пароль",
|
||||
"password_placeholder": "Введите пароль",
|
||||
"login_button": "Войти",
|
||||
"no_account": "Нет аккаунта?",
|
||||
"register": "Зарегистрироваться",
|
||||
"admin_setup": "Первый запуск?",
|
||||
"setup": "Настроить администратора",
|
||||
"register_title": "Создание аккаунта",
|
||||
"email": "Email",
|
||||
"email_placeholder": "Введите email",
|
||||
"confirm_password": "Подтвердите пароль",
|
||||
"confirm_password_placeholder": "Подтвердите пароль",
|
||||
"register_button": "Создать аккаунт",
|
||||
"have_account": "Уже есть аккаунт?",
|
||||
"login": "Войти",
|
||||
"setup_title": "Начальная настройка",
|
||||
"setup_step1": "Админ",
|
||||
"setup_step2": "Система",
|
||||
"setup_step3": "Готово",
|
||||
"admin_username": "Имя администратора",
|
||||
"admin_email": "Email администратора",
|
||||
"admin_password": "Пароль администратора",
|
||||
"create_admin": "Создать администратора",
|
||||
"back_to_login": "Уже настроено?",
|
||||
"admin_success": "Аккаунт администратора успешно создан! Теперь вы можете войти.",
|
||||
"account_success": "Аккаунт успешно создан! Теперь вы можете войти.",
|
||||
"passwords_mismatch": "Пароли не совпадают",
|
||||
"admin_create_error": "Ошибка создания аккаунта администратора",
|
||||
"or": "или",
|
||||
"sso_login": "Войти через SSO",
|
||||
"sso_login_provider": "Войти через {{provider}}"
|
||||
},
|
||||
"storage": {
|
||||
"title": "Хранилище",
|
||||
"calculating": "Вычисление...",
|
||||
"used": "{{percentage}}% использовано ({{used}} / {{total}})"
|
||||
},
|
||||
"viewer": {
|
||||
"unsupported_file": "Предварительный просмотр этого типа файлов недоступен.",
|
||||
"download_file": "Скачать файл",
|
||||
"zoom_in": "Увеличить",
|
||||
"zoom_out": "Уменьшить",
|
||||
"zoom_reset": "Сбросить масштаб"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "Добро пожаловать!",
|
||||
"subtitle": "Выберите язык для продолжения",
|
||||
"continue": "Продолжить",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"zh": "中文",
|
||||
"fa": "فارسی",
|
||||
"fr": "Français",
|
||||
"de": "Deutsch",
|
||||
"pt": "Português",
|
||||
"ru": "Русский"
|
||||
}
|
||||
},
|
||||
"favorites": {
|
||||
"empty_state": "Избранного пока нет",
|
||||
"empty_hint": "Добавьте файлы или папки в избранное, нажав на звёздочку",
|
||||
"add": "В избранное",
|
||||
"remove": "Из избранного",
|
||||
"added_title": "Добавлено в избранное",
|
||||
"added_msg": "добавлено в избранное",
|
||||
"removed_title": "Удалено из избранного",
|
||||
"removed_msg": "удалено из избранного"
|
||||
},
|
||||
"recent": {
|
||||
"title": "Недавние",
|
||||
"clear": "Очистить недавние",
|
||||
"accessed": "Открыт",
|
||||
"empty_state": "Нет недавних файлов",
|
||||
"empty_hint": "Открытые вами файлы будут отображаться здесь"
|
||||
},
|
||||
"notifications": {
|
||||
"file_renamed": "Файл переименован",
|
||||
"file_renamed_to": "Файл переименован в \"{{name}}\"",
|
||||
"folder_renamed": "Папка переименована",
|
||||
"folder_renamed_to": "Папка переименована в \"{{name}}\"",
|
||||
"file_uploaded": "Файл загружен",
|
||||
"file_deleted": "Файл перемещён в корзину",
|
||||
"folder_deleted": "Папка перемещена в корзину",
|
||||
"item_deleted_permanently": "Элемент удалён навсегда",
|
||||
"trash_emptied": "Корзина успешно очищена",
|
||||
"title": "Уведомления",
|
||||
"empty": "Нет уведомлений"
|
||||
},
|
||||
"batch": {
|
||||
"one_selected": "Выбран 1 элемент",
|
||||
"n_selected": "Выбрано {{count}} элементов",
|
||||
"confirm_delete": "Вы уверены, что хотите переместить {{count}} элементов в корзину?",
|
||||
"move_title": "Переместить {{count}} элементов",
|
||||
"add_favorites": "В избранное",
|
||||
"move_copy": "Переместить или копировать"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "Панель администратора",
|
||||
"back_to_app": "Назад в OxiCloud",
|
||||
"loading": "Загрузка…",
|
||||
"access_denied": "Доступ запрещён",
|
||||
"access_denied_desc": "Необходимы права администратора.",
|
||||
"sign_in": "Войти",
|
||||
"tab_dashboard": "Панель",
|
||||
"tab_users": "Пользователи",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "Всего пользователей",
|
||||
"active_users": "Активные",
|
||||
"admins": "Администраторы",
|
||||
"version": "Версия",
|
||||
"storage_overview": "Обзор хранилища",
|
||||
"used": "Использовано",
|
||||
"total_quota": "Общая квота",
|
||||
"usage_pct": "Использование %",
|
||||
"users_over_80": "Пользователи >80%",
|
||||
"users_over_quota": "Сверх квоты",
|
||||
"system": "Система",
|
||||
"auth_label": "Аутентификация",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "Квоты",
|
||||
"enabled": "Включено",
|
||||
"disabled": "Отключено",
|
||||
"active": "Активен",
|
||||
"off": "Выкл",
|
||||
"allow_registration": "Разрешить публичную регистрацию",
|
||||
"registration_warning": "Публичная регистрация отключена. Только админы могут создавать пользователей.",
|
||||
"user_management": "Управление пользователями",
|
||||
"create_user": "Создать пользователя",
|
||||
"col_user": "Пользователь",
|
||||
"col_role": "Роль",
|
||||
"col_auth": "Аутентификация",
|
||||
"col_status": "Статус",
|
||||
"col_storage": "Хранилище",
|
||||
"col_last_login": "Последний вход",
|
||||
"col_actions": "Действия",
|
||||
"loading_users": "Загрузка пользователей…",
|
||||
"failed_load_users": "Не удалось загрузить",
|
||||
"no_users_found": "Пользователи не найдены",
|
||||
"showing_users": "Показано {{from}}-{{to}} из {{total}}",
|
||||
"prev": "Назад",
|
||||
"next": "Далее",
|
||||
"inactive": "Неактивен",
|
||||
"you_badge": "(вы)",
|
||||
"local": "Локальный",
|
||||
"never": "Никогда",
|
||||
"just_now": "Только что",
|
||||
"minutes_ago": "{{n}} мин назад",
|
||||
"hours_ago": "{{n}} ч назад",
|
||||
"days_ago": "{{n}} дн назад",
|
||||
"edit_quota_title": "Изменить квоту",
|
||||
"reset_password_title": "Сбросить пароль",
|
||||
"toggle_role_title": "Сменить роль",
|
||||
"deactivate_title": "Деактивировать",
|
||||
"activate_title": "Активировать",
|
||||
"delete_title": "Удалить",
|
||||
"sso_title": "Единый вход (OIDC / SSO)",
|
||||
"enable_sso": "Включить SSO",
|
||||
"provider_name": "Имя провайдера",
|
||||
"issuer_url": "URL издателя",
|
||||
"issuer_url_hint": "URL издателя OpenID Connect",
|
||||
"auto_discover": "Авто-обнаружение",
|
||||
"discovering": "Обнаружение…",
|
||||
"client_id": "Client ID",
|
||||
"client_secret": "Client Secret",
|
||||
"client_secret_placeholder": "Оставьте пустым для сохранения",
|
||||
"secret_configured": "Client secret уже настроен",
|
||||
"callback_url": "URL обратного вызова",
|
||||
"callback_url_hint": "(зарегистрируйте в IdP)",
|
||||
"advanced_settings": "Расширенные настройки",
|
||||
"scopes": "Области",
|
||||
"auto_provision": "Автоматически создавать пользователей",
|
||||
"admin_groups": "Группы администраторов",
|
||||
"admin_groups_hint": "Имена групп OIDC через запятую",
|
||||
"disable_password": "Отключить вход по паролю (только OIDC)",
|
||||
"password_warning": "Это заблокирует ВСЕ входы по паролю!",
|
||||
"test_btn": "Тест",
|
||||
"save_btn": "Сохранить",
|
||||
"saving": "Сохранение…",
|
||||
"settings_saved": "Настройки сохранены — OIDC теперь {{status}}",
|
||||
"quota_modal_title": "Обновить квоту",
|
||||
"quota_user_label": "Пользователь:",
|
||||
"new_quota": "Новая квота",
|
||||
"quota_unlimited_hint": "0 для безлимитного",
|
||||
"cancel": "Отмена",
|
||||
"create_user_title": "Создать пользователя",
|
||||
"username_label": "Имя пользователя",
|
||||
"username_placeholder": "ivanov",
|
||||
"username_hint": "3–32 символа",
|
||||
"password_label": "Пароль",
|
||||
"password_placeholder": "Мин. 8 символов",
|
||||
"email_label": "Эл. почта",
|
||||
"email_optional": "(необязательно)",
|
||||
"email_placeholder": "user@example.com (автоматически если пусто)",
|
||||
"role_label": "Роль",
|
||||
"role_user": "Пользователь",
|
||||
"role_admin": "Админ",
|
||||
"quota_label": "Квота",
|
||||
"creating": "Создание…",
|
||||
"reset_pw_title": "Сбросить пароль",
|
||||
"new_password_label": "Новый пароль",
|
||||
"resetting": "Сброс…",
|
||||
"reset_btn": "Сбросить",
|
||||
"confirm_role_change": "Изменить роль на {{role}}?",
|
||||
"confirm_deactivate": "Деактивировать этого пользователя?",
|
||||
"confirm_activate": "Активировать этого пользователя?",
|
||||
"confirm_delete_user": "УДАЛИТЬ пользователя \"{{name}}\"? Нельзя отменить!",
|
||||
"confirm_action": "Подтвердить",
|
||||
"confirm_yes": "Подтвердить",
|
||||
"confirm_no": "Отмена",
|
||||
"error_username_short": "Имя минимум 3 символа",
|
||||
"error_password_short": "Пароль минимум 8 символов",
|
||||
"error_generic": "Ошибка",
|
||||
"error_network": "Ошибка сети: {{message}}",
|
||||
"error_create_user": "Не удалось создать"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "Профиль",
|
||||
"back_to_app": "Назад в OxiCloud",
|
||||
"loading": "Загрузка…",
|
||||
"not_authenticated": "Не аутентифицирован",
|
||||
"not_authenticated_desc": "Войдите, чтобы просмотреть свой профиль.",
|
||||
"sign_in": "Войти",
|
||||
"role_admin": "Администратор",
|
||||
"role_user": "Пользователь",
|
||||
"account_details": "Данные аккаунта",
|
||||
"username": "Имя пользователя",
|
||||
"email": "Эл. почта",
|
||||
"role": "Роль",
|
||||
"last_login": "Последний вход",
|
||||
"storage": "Хранилище",
|
||||
"used": "Использовано",
|
||||
"quota": "Квота",
|
||||
"usage": "Использование",
|
||||
"unlimited": "Безлимитный",
|
||||
"app_passwords": "Пароли приложений",
|
||||
"app_pw_desc": "Создайте пароли для клиентов WebDAV, CalDAV и CardDAV. Каждый пароль показывается только один раз.",
|
||||
"app_pw_label_placeholder": "Метка (напр. Thunderbird, macOS)",
|
||||
"generate": "Создать",
|
||||
"generating": "Создание…",
|
||||
"new_password_for": "Новый пароль для",
|
||||
"copy_warning": "Скопируйте пароль сейчас. Вы не сможете увидеть его снова.",
|
||||
"copy_to_clipboard": "Копировать в буфер",
|
||||
"col_label": "Метка",
|
||||
"col_created": "Создан",
|
||||
"col_last_used": "Последнее использование",
|
||||
"col_status": "Статус",
|
||||
"active": "Активен",
|
||||
"revoked": "Отозван",
|
||||
"revoke_title": "Отозвать",
|
||||
"no_app_passwords": "Паролей приложений пока нет.",
|
||||
"client_sessions": "Сессии клиентов",
|
||||
"client_sessions_desc": "Автоматически создаются при подключении клиента, совместимого с Nextcloud.",
|
||||
"col_client": "Клиент",
|
||||
"never": "Никогда",
|
||||
"just_now": "Только что",
|
||||
"minutes_ago": "{{n}} мин назад",
|
||||
"hours_ago": "{{n}} ч назад",
|
||||
"days_ago": "{{n}} дн назад",
|
||||
"change_password": "Изменить пароль",
|
||||
"current_password": "Текущий пароль",
|
||||
"new_password": "Новый пароль",
|
||||
"min_8_chars": "Минимум 8 символов",
|
||||
"confirm_password": "Подтвердите новый пароль",
|
||||
"update_password": "Обновить пароль",
|
||||
"updating": "Обновление…",
|
||||
"password_updated": "Пароль успешно обновлён",
|
||||
"passwords_no_match": "Пароли не совпадают",
|
||||
"password_too_short": "Пароль должен быть не менее 8 символов",
|
||||
"password_change_failed": "Не удалось изменить пароль",
|
||||
"error_network": "Ошибка сети: {{message}}",
|
||||
"error_label_required": "Введите метку",
|
||||
"error_create_pw": "Не удалось создать пароль",
|
||||
"confirm_revoke": "Отозвать пароль «{{label}}»? Клиенты перестанут работать.",
|
||||
"error_revoke": "Не удалось отозвать"
|
||||
}
|
||||
}
|
||||
+183
-2
@@ -142,6 +142,8 @@
|
||||
"size": "大小",
|
||||
"modified": "修改日期",
|
||||
"no_files": "此文件夹中没有文件",
|
||||
"empty_hint": "上传文件或创建文件夹以开始使用",
|
||||
"loading": "正在加载文件…",
|
||||
"view_grid": "网格视图",
|
||||
"view_list": "列表视图",
|
||||
"file_types": {
|
||||
@@ -265,8 +267,8 @@
|
||||
"zoom_reset": "重置缩放"
|
||||
},
|
||||
"language_selector": {
|
||||
"title": "欢迎使用 OxiCloud",
|
||||
"subtitle": "请选择您的语言",
|
||||
"title": "欢迎!",
|
||||
"subtitle": "选择您的语言以继续",
|
||||
"continue": "继续",
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -302,5 +304,184 @@
|
||||
"move_title": "移动 {{count}} 个项目",
|
||||
"add_favorites": "添加到收藏夹",
|
||||
"move_copy": "移动或复制"
|
||||
},
|
||||
"admin": {
|
||||
"page_title": "管理面板",
|
||||
"back_to_app": "返回 OxiCloud",
|
||||
"loading": "加载中…",
|
||||
"access_denied": "拒绝访问",
|
||||
"access_denied_desc": "需要管理员权限。",
|
||||
"sign_in": "登录",
|
||||
"tab_dashboard": "仪表盘",
|
||||
"tab_users": "用户",
|
||||
"tab_oidc": "SSO / OIDC",
|
||||
"total_users": "用户总数",
|
||||
"active_users": "活跃用户",
|
||||
"admins": "管理员",
|
||||
"version": "版本",
|
||||
"storage_overview": "存储概览",
|
||||
"used": "已使用",
|
||||
"total_quota": "总配额",
|
||||
"usage_pct": "使用率",
|
||||
"users_over_80": "超过80%配额",
|
||||
"users_over_quota": "超过配额",
|
||||
"system": "系统",
|
||||
"auth_label": "认证",
|
||||
"oidc_label": "OIDC",
|
||||
"quotas_label": "配额",
|
||||
"enabled": "已启用",
|
||||
"disabled": "已禁用",
|
||||
"active": "活跃",
|
||||
"off": "关闭",
|
||||
"allow_registration": "允许公开自助注册",
|
||||
"registration_warning": "公开注册已禁用。只有管理员可以创建新用户。",
|
||||
"user_management": "用户管理",
|
||||
"create_user": "创建用户",
|
||||
"col_user": "用户",
|
||||
"col_role": "角色",
|
||||
"col_auth": "认证",
|
||||
"col_status": "状态",
|
||||
"col_storage": "存储",
|
||||
"col_last_login": "最后登录",
|
||||
"col_actions": "操作",
|
||||
"loading_users": "正在加载用户…",
|
||||
"failed_load_users": "加载失败",
|
||||
"no_users_found": "未找到用户",
|
||||
"showing_users": "显示 {{from}}-{{to}} / {{total}}",
|
||||
"prev": "上一页",
|
||||
"next": "下一页",
|
||||
"inactive": "未激活",
|
||||
"you_badge": "(你)",
|
||||
"local": "本地",
|
||||
"never": "从未",
|
||||
"just_now": "刚刚",
|
||||
"minutes_ago": "{{n}}分钟前",
|
||||
"hours_ago": "{{n}}小时前",
|
||||
"days_ago": "{{n}}天前",
|
||||
"edit_quota_title": "编辑配额",
|
||||
"reset_password_title": "重置密码",
|
||||
"toggle_role_title": "切换角色",
|
||||
"deactivate_title": "停用",
|
||||
"activate_title": "启用",
|
||||
"delete_title": "删除",
|
||||
"sso_title": "单点登录 (OIDC / SSO)",
|
||||
"enable_sso": "启用 SSO 认证",
|
||||
"provider_name": "提供商名称",
|
||||
"issuer_url": "发行者 URL",
|
||||
"issuer_url_hint": "您的身份提供商的 OpenID Connect 发行者 URL",
|
||||
"auto_discover": "自动发现",
|
||||
"discovering": "发现中…",
|
||||
"client_id": "客户端 ID",
|
||||
"client_secret": "客户端密钥",
|
||||
"client_secret_placeholder": "留空以保留当前值",
|
||||
"secret_configured": "已配置客户端密钥",
|
||||
"callback_url": "回调 URL",
|
||||
"callback_url_hint": "(在您的 IdP 中注册)",
|
||||
"advanced_settings": "高级设置",
|
||||
"scopes": "范围",
|
||||
"auto_provision": "首次登录时自动配置用户",
|
||||
"admin_groups": "管理组",
|
||||
"admin_groups_hint": "映射到管理员角色的逗号分隔 OIDC 组名",
|
||||
"disable_password": "禁用密码登录 (仅 OIDC)",
|
||||
"password_warning": "这将阻止所有基于密码的登录!",
|
||||
"test_btn": "测试",
|
||||
"save_btn": "保存",
|
||||
"saving": "保存中…",
|
||||
"settings_saved": "设置已保存 — OIDC 现在 {{status}}",
|
||||
"quota_modal_title": "更新存储配额",
|
||||
"quota_user_label": "用户:",
|
||||
"new_quota": "新配额",
|
||||
"quota_unlimited_hint": "0表示无限制",
|
||||
"cancel": "取消",
|
||||
"create_user_title": "创建新用户",
|
||||
"username_label": "用户名",
|
||||
"username_placeholder": "zhangsan",
|
||||
"username_hint": "3–32个字符",
|
||||
"password_label": "密码",
|
||||
"password_placeholder": "至少8个字符",
|
||||
"email_label": "邮箱",
|
||||
"email_optional": "(可选)",
|
||||
"email_placeholder": "user@example.com (留空自动生成)",
|
||||
"role_label": "角色",
|
||||
"role_user": "用户",
|
||||
"role_admin": "管理员",
|
||||
"quota_label": "配额",
|
||||
"creating": "创建中…",
|
||||
"reset_pw_title": "重置密码",
|
||||
"new_password_label": "新密码",
|
||||
"resetting": "重置中…",
|
||||
"reset_btn": "重置",
|
||||
"confirm_role_change": "将角色更改为 {{role}}?",
|
||||
"confirm_deactivate": "确定要停用此用户吗?",
|
||||
"confirm_activate": "确定要启用此用户吗?",
|
||||
"confirm_delete_user": "删除用户 \"{{name}}\"?此操作无法撤消!",
|
||||
"confirm_action": "确认操作",
|
||||
"confirm_yes": "确认",
|
||||
"confirm_no": "取消",
|
||||
"error_username_short": "用户名至少需要3个字符",
|
||||
"error_password_short": "密码至少需要8个字符",
|
||||
"error_generic": "失败",
|
||||
"error_network": "网络错误:{{message}}",
|
||||
"error_create_user": "创建用户失败"
|
||||
},
|
||||
"profile": {
|
||||
"page_title": "个人资料",
|
||||
"back_to_app": "返回 OxiCloud",
|
||||
"loading": "加载中…",
|
||||
"not_authenticated": "未认证",
|
||||
"not_authenticated_desc": "请登录以查看您的个人资料。",
|
||||
"sign_in": "登录",
|
||||
"role_admin": "管理员",
|
||||
"role_user": "用户",
|
||||
"account_details": "账户详情",
|
||||
"username": "用户名",
|
||||
"email": "邮箱",
|
||||
"role": "角色",
|
||||
"last_login": "最后登录",
|
||||
"storage": "存储",
|
||||
"used": "已使用",
|
||||
"quota": "配额",
|
||||
"usage": "使用率",
|
||||
"unlimited": "无限制",
|
||||
"app_passwords": "应用密码",
|
||||
"app_pw_desc": "为 WebDAV、CalDAV 和 CardDAV 客户端生成密码。每个密码只显示一次。",
|
||||
"app_pw_label_placeholder": "标签(如 Thunderbird、macOS)",
|
||||
"generate": "生成",
|
||||
"generating": "生成中…",
|
||||
"new_password_for": "新密码用于",
|
||||
"copy_warning": "请立即复制此密码,之后将无法再次查看。",
|
||||
"copy_to_clipboard": "复制到剪贴板",
|
||||
"col_label": "标签",
|
||||
"col_created": "创建时间",
|
||||
"col_last_used": "最后使用",
|
||||
"col_status": "状态",
|
||||
"active": "活跃",
|
||||
"revoked": "已撤销",
|
||||
"revoke_title": "撤销",
|
||||
"no_app_passwords": "暂无应用密码。",
|
||||
"client_sessions": "客户端会话",
|
||||
"client_sessions_desc": "连接 Nextcloud 兼容客户端时自动生成。",
|
||||
"col_client": "客户端",
|
||||
"never": "从未",
|
||||
"just_now": "刚刚",
|
||||
"minutes_ago": "{{n}}分钟前",
|
||||
"hours_ago": "{{n}}小时前",
|
||||
"days_ago": "{{n}}天前",
|
||||
"change_password": "修改密码",
|
||||
"current_password": "当前密码",
|
||||
"new_password": "新密码",
|
||||
"min_8_chars": "至少8个字符",
|
||||
"confirm_password": "确认新密码",
|
||||
"update_password": "更新密码",
|
||||
"updating": "更新中…",
|
||||
"password_updated": "密码更新成功",
|
||||
"passwords_no_match": "密码不匹配",
|
||||
"password_too_short": "密码至少需要8个字符",
|
||||
"password_change_failed": "修改密码失败",
|
||||
"error_network": "网络错误:{{message}}",
|
||||
"error_label_required": "请输入标签",
|
||||
"error_create_pw": "创建应用密码失败",
|
||||
"confirm_revoke": "撤销应用密码\"{{label}}\"?使用此密码的客户端将停止工作。",
|
||||
"error_revoke": "撤销失败"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@
|
||||
<div class="auth-logo-text">OxiCloud</div>
|
||||
</div>
|
||||
|
||||
<h2 class="auth-title" id="language-title">Welcome to OxiCloud</h2>
|
||||
<p class="language-subtitle" id="language-subtitle">Please select your language</p>
|
||||
<h2 class="auth-title" id="language-title">Welcome!</h2>
|
||||
<p class="language-subtitle" id="language-subtitle">Select your language to continue</p>
|
||||
|
||||
<!-- Single selected language box with inline dropdown -->
|
||||
<div class="lang-picker" id="lang-picker">
|
||||
|
||||
+34
-33
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OxiCloud — My Profile</title>
|
||||
<script src="/js/core/theme-init.js"></script>
|
||||
<script src="/js/core/i18n.js" defer></script>
|
||||
<script src="/js/core/icons.js" defer></script>
|
||||
<script src="/js/core/csrf.js" defer></script>
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
@@ -22,20 +23,20 @@
|
||||
</div>
|
||||
<span class="profile-title-text">OxiCloud</span>
|
||||
</a>
|
||||
<span class="profile-title-separator">· Profile</span>
|
||||
<span class="profile-title-separator">· <span data-i18n="profile.page_title">Profile</span></span>
|
||||
</div>
|
||||
<div class="profile-header-right">
|
||||
<a href="/"><i class="fas fa-arrow-left"></i> Back to OxiCloud</a>
|
||||
<a href="/"><i class="fas fa-arrow-left"></i> <span data-i18n="profile.back_to_app">Back to OxiCloud</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-container">
|
||||
<div id="loading"><i class="fas fa-circle-notch"></i> Loading…</div>
|
||||
<div id="loading"><i class="fas fa-circle-notch"></i> <span data-i18n="profile.loading">Loading…</span></div>
|
||||
<div id="auth-error" class="hidden">
|
||||
<div class="err-icon"><i class="fas fa-lock"></i></div>
|
||||
<h2>Not Authenticated</h2>
|
||||
<p>Please sign in to view your profile.</p>
|
||||
<a href="/login"><i class="fas fa-sign-in-alt"></i> Sign in</a>
|
||||
<h2 data-i18n="profile.not_authenticated">Not Authenticated</h2>
|
||||
<p data-i18n="profile.not_authenticated_desc">Please sign in to view your profile.</p>
|
||||
<a href="/login"><i class="fas fa-sign-in-alt"></i> <span data-i18n="profile.sign_in">Sign in</span></a>
|
||||
</div>
|
||||
|
||||
<div id="main-content" class="hidden">
|
||||
@@ -51,41 +52,41 @@
|
||||
</div>
|
||||
|
||||
<div class="profile-card">
|
||||
<h2><i class="fas fa-id-card"></i> Account Details</h2>
|
||||
<h2><i class="fas fa-id-card"></i> <span data-i18n="profile.account_details">Account Details</span></h2>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label"><i class="fas fa-user"></i> Username</div>
|
||||
<div class="info-label"><i class="fas fa-user"></i> <span data-i18n="profile.username">Username</span></div>
|
||||
<div class="info-value" id="p-detail-username">—</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label"><i class="fas fa-envelope"></i> Email</div>
|
||||
<div class="info-label"><i class="fas fa-envelope"></i> <span data-i18n="profile.email">Email</span></div>
|
||||
<div class="info-value" id="p-detail-email">—</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label"><i class="fas fa-shield-alt"></i> Role</div>
|
||||
<div class="info-label"><i class="fas fa-shield-alt"></i> <span data-i18n="profile.role">Role</span></div>
|
||||
<div class="info-value" id="p-detail-role">—</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label"><i class="fas fa-clock"></i> Last Login</div>
|
||||
<div class="info-label"><i class="fas fa-clock"></i> <span data-i18n="profile.last_login">Last Login</span></div>
|
||||
<div class="info-value" id="p-detail-login">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-card">
|
||||
<h2><i class="fas fa-hdd"></i> Storage</h2>
|
||||
<h2><i class="fas fa-hdd"></i> <span data-i18n="profile.storage">Storage</span></h2>
|
||||
<div class="storage-stats">
|
||||
<div class="storage-stat">
|
||||
<div class="stat-value" id="p-storage-used">—</div>
|
||||
<div class="stat-label">Used</div>
|
||||
<div class="stat-label" data-i18n="profile.used">Used</div>
|
||||
</div>
|
||||
<div class="storage-stat">
|
||||
<div class="stat-value" id="p-storage-quota">—</div>
|
||||
<div class="stat-label">Quota</div>
|
||||
<div class="stat-label" data-i18n="profile.quota">Quota</div>
|
||||
</div>
|
||||
<div class="storage-stat">
|
||||
<div class="stat-value" id="p-storage-pct">—</div>
|
||||
<div class="stat-label">Usage</div>
|
||||
<div class="stat-label" data-i18n="profile.usage">Usage</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="storage-bar-wrap">
|
||||
@@ -95,44 +96,44 @@
|
||||
</div>
|
||||
|
||||
<div class="profile-card" id="app-passwords-section">
|
||||
<h2><i class="fas fa-key"></i> App Passwords</h2>
|
||||
<p class="app-pw-desc">Generate passwords for WebDAV, CalDAV, and CardDAV clients. Each password is shown only once.</p>
|
||||
<h2><i class="fas fa-key"></i> <span data-i18n="profile.app_passwords">App Passwords</span></h2>
|
||||
<p class="app-pw-desc" data-i18n="profile.app_pw_desc">Generate passwords for WebDAV, CalDAV, and CardDAV clients. Each password is shown only once.</p>
|
||||
|
||||
<div class="app-pw-create">
|
||||
<input type="text" id="app-pw-label" placeholder="Label (e.g. Thunderbird, macOS)" maxlength="128">
|
||||
<button class="btn btn-primary" id="app-pw-generate"><i class="fas fa-plus"></i> Generate</button>
|
||||
<input type="text" id="app-pw-label" data-i18n-placeholder="profile.app_pw_label_placeholder" placeholder="Label (e.g. Thunderbird, macOS)" maxlength="128">
|
||||
<button class="btn btn-primary" id="app-pw-generate"><i class="fas fa-plus"></i> <span data-i18n="profile.generate">Generate</span></button>
|
||||
</div>
|
||||
|
||||
<div id="app-pw-created" class="app-pw-created hidden">
|
||||
<div class="app-pw-created-label">New password for <strong id="app-pw-created-label"></strong>:</div>
|
||||
<div class="app-pw-created-label"><span data-i18n="profile.new_password_for">New password for</span> <strong id="app-pw-created-label"></strong>:</div>
|
||||
<div class="app-pw-created-value">
|
||||
<code id="app-pw-created-password"></code>
|
||||
<button class="btn btn-copy" id="app-pw-copy-btn" title="Copy to clipboard"><i class="fas fa-copy"></i></button>
|
||||
<button class="btn btn-copy" id="app-pw-copy-btn" data-i18n-title="profile.copy_to_clipboard" title="Copy to clipboard"><i class="fas fa-copy"></i></button>
|
||||
</div>
|
||||
<small>Copy this password now. You won't be able to see it again.</small>
|
||||
<small data-i18n="profile.copy_warning">Copy this password now. You won't be able to see it again.</small>
|
||||
</div>
|
||||
|
||||
<div id="app-pw-status"></div>
|
||||
|
||||
<table class="app-pw-table" id="app-pw-table">
|
||||
<thead>
|
||||
<tr><th>Label</th><th>Created</th><th>Last Used</th><th>Status</th><th></th></tr>
|
||||
<tr><th data-i18n="profile.col_label">Label</th><th data-i18n="profile.col_created">Created</th><th data-i18n="profile.col_last_used">Last Used</th><th data-i18n="profile.col_status">Status</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody id="app-pw-tbody"></tbody>
|
||||
</table>
|
||||
<div id="app-pw-empty" class="app-pw-empty hidden">No app passwords yet.</div>
|
||||
<div id="app-pw-empty" class="app-pw-empty hidden" data-i18n="profile.no_app_passwords">No app passwords yet.</div>
|
||||
|
||||
<div id="app-pw-auto-section" class="app-pw-auto-section hidden">
|
||||
<button class="app-pw-auto-toggle" id="app-pw-auto-toggle">
|
||||
<i class="fas fa-chevron-right" id="app-pw-auto-chevron"></i>
|
||||
<span>Client sessions</span>
|
||||
<span data-i18n="profile.client_sessions">Client sessions</span>
|
||||
<span class="app-pw-auto-count" id="app-pw-auto-count">0</span>
|
||||
</button>
|
||||
<div id="app-pw-auto-body" class="hidden">
|
||||
<p class="app-pw-auto-desc">Auto-generated when you connect a Nextcloud-compatible client.</p>
|
||||
<p class="app-pw-auto-desc" data-i18n="profile.client_sessions_desc">Auto-generated when you connect a Nextcloud-compatible client.</p>
|
||||
<table class="app-pw-table" id="app-pw-auto-table">
|
||||
<thead>
|
||||
<tr><th>Client</th><th>Created</th><th>Last Used</th><th></th></tr>
|
||||
<tr><th data-i18n="profile.col_client">Client</th><th data-i18n="profile.col_created">Created</th><th data-i18n="profile.col_last_used">Last Used</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody id="app-pw-auto-tbody"></tbody>
|
||||
</table>
|
||||
@@ -141,22 +142,22 @@
|
||||
</div>
|
||||
|
||||
<div class="profile-card" id="password-section">
|
||||
<h2><i class="fas fa-key"></i> Change Password</h2>
|
||||
<h2><i class="fas fa-key"></i> <span data-i18n="profile.change_password">Change Password</span></h2>
|
||||
<form id="password-form">
|
||||
<div class="form-group">
|
||||
<label for="current-password">Current Password</label>
|
||||
<label for="current-password" data-i18n="profile.current_password">Current Password</label>
|
||||
<input type="password" id="current-password" required autocomplete="current-password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-password">New Password</label>
|
||||
<label for="new-password" data-i18n="profile.new_password">New Password</label>
|
||||
<input type="password" id="new-password" required minlength="8" autocomplete="new-password">
|
||||
<small>At least 8 characters</small>
|
||||
<small data-i18n="profile.min_8_chars">At least 8 characters</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm-password">Confirm New Password</label>
|
||||
<label for="confirm-password" data-i18n="profile.confirm_password">Confirm New Password</label>
|
||||
<input type="password" id="confirm-password" required minlength="8" autocomplete="new-password">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" id="pw-submit"><i class="fas fa-save"></i> Update Password</button>
|
||||
<button type="submit" class="btn btn-primary" id="pw-submit"><i class="fas fa-save"></i> <span data-i18n="profile.update_password">Update Password</span></button>
|
||||
<div id="pw-status"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user