feat: add admin/profile UI, i18n (fr/de/pt), fix CI pipelines

- Add admin panel link and profile modal in user menu dropdown
- Add French, German and Portuguese locale support (full translations)
- Register new locales across JS frontend and Rust backend
- Create CI pipeline (fmt, clippy, test, audit, build)
- Fix docker-build.yml (cache, real tests, reduced timeout)
- Fix docker-publish.yml (multi-arch via QEMU, latest tag, pre-publish tests)
- Add dependabot.yml for automated dependency updates
This commit is contained in:
Dionisio
2026-02-11 12:26:29 +01:00
parent 6b55cdf3c4
commit 3050955f9c
21 changed files with 1422 additions and 408 deletions
+40
View File
@@ -0,0 +1,40 @@
version: 2
updates:
# Rust (Cargo) dependencies
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 10
labels:
- "dependencies"
- "rust"
commit-message:
prefix: "deps(rust):"
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 5
labels:
- "dependencies"
- "ci"
commit-message:
prefix: "deps(ci):"
# Docker
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 3
labels:
- "dependencies"
- "docker"
commit-message:
prefix: "deps(docker):"
+85
View File
@@ -0,0 +1,85 @@
name: CI
on:
push:
branches: [ "main", "dev" ]
pull_request:
branches: [ "main", "dev" ]
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-Dwarnings"
DATABASE_URL: "postgres://postgres:postgres@localhost/oxicloud_test"
jobs:
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy --all-targets --all-features -- -D warnings
test:
name: Tests
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: oxicloud_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Initialize test database
run: psql -h localhost -U postgres -d oxicloud_test -f db/schema.sql
env:
PGPASSWORD: postgres
- name: Run tests
run: cargo test --all-features --workspace
env:
DATABASE_URL: "postgres://postgres:postgres@localhost/oxicloud_test"
audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: rustsec/audit-check@v2.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
build:
name: Build Check
runs-on: ubuntu-latest
needs: [fmt, clippy, test]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo build --release
+34 -14
View File
@@ -1,6 +1,5 @@
name: Docker Build and Test
# Trigger on push to main branch or on pull requests
on:
push:
branches: [ "main", "dev" ]
@@ -8,21 +7,18 @@ on:
branches: [ "main", "dev" ]
jobs:
# Build and test Docker image but don't push
build-and-test:
name: Build and Test Docker Image
runs-on: ubuntu-latest
timeout-minutes: 120 # Aumentado a 2 horas para dar más tiempo a la compilación
timeout-minutes: 45
steps:
- name: Checkout code
uses: actions/checkout@v4
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Extract metadata (tags, labels) for Docker
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
@@ -33,7 +29,6 @@ jobs:
type=ref,event=pr
type=sha
# Build Docker image but don't push
- name: Build Docker image
uses: docker/build-push-action@v5
with:
@@ -41,16 +36,41 @@ jobs:
push: false
load: true
tags: test/oxicloud:test
# Caché eliminado para evitar problemas de expiración de token
cache-from: type=gha
cache-to: type=gha,mode=max
# Run some basic tests against the built image
- name: Test Docker image
- name: Verify image starts correctly
run: |
docker run --rm test/oxicloud:test --version || true
docker run --rm test/oxicloud:test --help || true
# Start the container in background
docker run -d --name oxicloud-test \
-e DATABASE_URL="postgres://test:test@localhost/test" \
-p 8080:8080 \
test/oxicloud:test || true
# Verify the image structure
echo "✅ Checking Docker image layers and size"
docker image inspect test/oxicloud:test
# Give it a moment to start (or fail)
sleep 3
# Check that the container exists and show logs
echo "=== Container status ==="
docker ps -a --filter name=oxicloud-test
echo "=== Container logs ==="
docker logs oxicloud-test 2>&1 || true
# Cleanup
docker rm -f oxicloud-test || true
- name: Verify image structure
run: |
echo "=== Image size ==="
docker image ls test/oxicloud:test
echo "=== Binary exists and is executable ==="
docker run --rm --entrypoint sh test/oxicloud:test -c "test -x /usr/local/bin/oxicloud && echo 'OK: binary is executable'"
echo "=== Static files present ==="
docker run --rm --entrypoint sh test/oxicloud:test -c "ls /app/static/index.html && echo 'OK: static files present'"
echo "=== DB schema present ==="
docker run --rm --entrypoint sh test/oxicloud:test -c "ls /app/db/schema.sql && echo 'OK: schema present'"
echo "✅ Docker build and test completed successfully"
+45 -48
View File
@@ -6,63 +6,47 @@ on:
workflow_dispatch:
inputs:
version:
description: 'Versión del release'
required: false
default: '0.1.0-rc1'
description: 'Version tag to publish (e.g. v0.3.0)'
required: true
env:
REGISTRY_IMAGE: diocrafts/oxicloud
jobs:
build-and-push:
# Run tests before publishing
test:
name: Pre-publish Tests
runs-on: ubuntu-latest
strategy:
matrix:
arch: [amd64, arm64]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test --workspace
# Build and push multi-arch image in a single job
build-and-push:
name: Build & Push Multi-Arch
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set version tag
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
VERSION="${{ github.event.inputs.version }}"
else
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
VERSION="${GITHUB_REF#refs/tags/}"
fi
# Strip leading 'v' if present for Docker tag
VERSION="${VERSION#v}"
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/${{ matrix.arch }}
push: true
tags: |
diocrafts/oxicloud:${{ env.VERSION }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ env.VERSION }}
# Crear el manifiesto multi-arch una vez todas las imágenes estén listas
manifest:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Set version tag
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
else
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -73,9 +57,22 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create and Push Manifest
- name: Build and Push Multi-Arch Image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.REGISTRY_IMAGE }}:${{ env.VERSION }}
${{ env.REGISTRY_IMAGE }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ env.VERSION }}
- name: Verify published image
run: |
docker buildx imagetools create \
-t diocrafts/oxicloud:${{ env.VERSION }} \
diocrafts/oxicloud:${{ env.VERSION }}@linux/amd64 \
diocrafts/oxicloud:${{ env.VERSION }}@linux/arm64
docker pull ${{ env.REGISTRY_IMAGE }}:${{ env.VERSION }}
docker image inspect ${{ env.REGISTRY_IMAGE }}:${{ env.VERSION }}
echo "✅ Image ${{ env.REGISTRY_IMAGE }}:${{ env.VERSION }} published successfully"
+3
View File
@@ -16,6 +16,9 @@ impl From<Locale> for LocaleDto {
let (code, name) = match locale {
Locale::English => ("en", "English"),
Locale::Spanish => ("es", "Español"),
Locale::French => ("fr", "Français"),
Locale::German => ("de", "Deutsch"),
Locale::Portuguese => ("pt", "Português"),
};
Self {
+9
View File
@@ -456,6 +456,15 @@ impl AppServiceFactory {
if let Err(e) = i18n_service.load_translations(Locale::Spanish).await {
tracing::warn!("Failed to load Spanish translations: {}", e);
}
if let Err(e) = i18n_service.load_translations(Locale::French).await {
tracing::warn!("Failed to load French translations: {}", e);
}
if let Err(e) = i18n_service.load_translations(Locale::German).await {
tracing::warn!("Failed to load German translations: {}", e);
}
if let Err(e) = i18n_service.load_translations(Locale::Portuguese).await {
tracing::warn!("Failed to load Portuguese translations: {}", e);
}
tracing::info!("Translations preloaded");
}
+9
View File
@@ -22,6 +22,9 @@ pub type I18nResult<T> = Result<T, I18nError>;
pub enum Locale {
English,
Spanish,
French,
German,
Portuguese,
}
impl Locale {
@@ -30,6 +33,9 @@ impl Locale {
match self {
Locale::English => "en",
Locale::Spanish => "es",
Locale::French => "fr",
Locale::German => "de",
Locale::Portuguese => "pt",
}
}
@@ -38,6 +44,9 @@ impl Locale {
match code.to_lowercase().as_str() {
"en" => Some(Locale::English),
"es" => Some(Locale::Spanish),
"fr" => Some(Locale::French),
"de" => Some(Locale::German),
"pt" => Some(Locale::Portuguese),
_ => None,
}
}
@@ -139,7 +139,7 @@ impl I18nService for FileSystemI18nService {
}
async fn available_locales(&self) -> Vec<Locale> {
vec![Locale::English, Locale::Spanish]
vec![Locale::English, Locale::Spanish, Locale::French, Locale::German, Locale::Portuguese]
}
async fn is_supported(&self, locale: Locale) -> bool {
+35
View File
@@ -621,6 +621,41 @@ select:focus {
transform: translateX(18px);
}
.user-menu-admin {
color: #1d4ed8;
}
.user-menu-admin i {
color: #3b82f6;
}
.user-menu-admin:hover {
background: #eff6ff;
}
.user-menu-role-badge {
padding: 0 20px 4px;
}
.role-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 11px;
font-weight: 600;
padding: 2px 10px;
border-radius: 10px;
}
.role-badge-admin {
background: #dbeafe;
color: #1d4ed8;
}
.role-badge i {
font-size: 10px;
}
.user-menu-logout {
color: #ef4444;
margin-bottom: 4px;
+12
View File
@@ -114,6 +114,9 @@
<div class="user-menu-email" id="user-menu-email">usuario@oxicloud.app</div>
</div>
</div>
<div class="user-menu-role-badge" id="user-menu-role-badge" style="display:none">
<span class="role-badge role-badge-admin"><i class="fas fa-shield-alt"></i> Admin</span>
</div>
<div class="user-menu-storage">
<div class="user-menu-storage-label">
<i class="fas fa-database"></i>
@@ -125,6 +128,15 @@
<div class="user-menu-storage-text" id="user-menu-storage-text">0% usado</div>
</div>
<div class="user-menu-divider"></div>
<button class="user-menu-item user-menu-admin" id="user-menu-admin" style="display:none">
<i class="fas fa-cogs"></i>
<span data-i18n="user_menu.admin_panel">Panel de administración</span>
</button>
<button class="user-menu-item" id="user-menu-profile">
<i class="fas fa-user-circle"></i>
<span data-i18n="user_menu.profile">Mi perfil</span>
</button>
<div class="user-menu-divider" id="user-menu-admin-divider" style="display:none"></div>
<button class="user-menu-item" id="user-menu-theme">
<i class="fas fa-moon"></i>
<span data-i18n="user_menu.appearance">Apariencia</span>
+95
View File
@@ -143,6 +143,10 @@ function setupUserMenu() {
const logoutBtn = document.getElementById('user-menu-logout');
const themeBtn = document.getElementById('user-menu-theme');
const aboutBtn = document.getElementById('user-menu-about');
const adminBtn = document.getElementById('user-menu-admin');
const adminDivider = document.getElementById('user-menu-admin-divider');
const profileBtn = document.getElementById('user-menu-profile');
const roleBadge = document.getElementById('user-menu-role-badge');
if (!wrapper || !avatarBtn || !menu) return;
@@ -153,6 +157,13 @@ function setupUserMenu() {
wrapper.classList.toggle('open');
if (!isOpen) {
updateUserMenuData();
// Show/hide admin panel button based on user role
const USER_DATA_KEY = 'oxicloud_user';
const userData = JSON.parse(localStorage.getItem(USER_DATA_KEY) || '{}');
const isAdmin = userData.role === 'admin';
if (adminBtn) adminBtn.style.display = isAdmin ? 'flex' : 'none';
if (adminDivider) adminDivider.style.display = isAdmin ? 'block' : 'none';
if (roleBadge) roleBadge.style.display = isAdmin ? 'block' : 'none';
}
});
@@ -192,6 +203,22 @@ function setupUserMenu() {
});
}
// Admin panel link
if (adminBtn) {
adminBtn.addEventListener('click', () => {
wrapper.classList.remove('open');
window.location.href = '/admin.html';
});
}
// Profile button — shows user info modal
if (profileBtn) {
profileBtn.addEventListener('click', () => {
wrapper.classList.remove('open');
showUserProfileModal();
});
}
// About modal
if (aboutBtn) {
aboutBtn.addEventListener('click', () => {
@@ -1417,6 +1444,74 @@ async function refreshUserData() {
// Expose refreshUserData globally
window.refreshUserData = refreshUserData;
/**
* Show User Profile modal with account details
*/
function showUserProfileModal() {
const USER_DATA_KEY = 'oxicloud_user';
const userData = JSON.parse(localStorage.getItem(USER_DATA_KEY) || '{}');
const username = userData.username || 'Usuario';
const email = userData.email || '';
const role = userData.role || 'user';
const initials = username.substring(0, 2).toUpperCase();
const usedBytes = userData.storage_used_bytes || 0;
const quotaBytes = userData.storage_quota_bytes || 0;
const percentage = quotaBytes > 0 ? Math.min(Math.round((usedBytes / quotaBytes) * 100), 100) : 0;
const barColor = percentage > 90 ? '#ef4444' : percentage > 70 ? '#f59e0b' : '#22c55e';
const t = (key, fallback) => (window.i18n && window.i18n.t) ? window.i18n.t(key) || fallback : fallback;
// Remove existing modal if any
const existing = document.getElementById('profile-modal-overlay');
if (existing) existing.remove();
const overlay = document.createElement('div');
overlay.id = 'profile-modal-overlay';
overlay.className = 'about-modal-overlay';
overlay.innerHTML = `
<div class="about-modal" style="max-width:380px">
<div style="text-align:center;padding:20px 20px 0">
<div style="width:64px;height:64px;border-radius:50%;background:linear-gradient(135deg,#3b82f6,#6366f1);color:#fff;display:inline-flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;margin-bottom:12px">${initials}</div>
<h3 style="margin:0;font-size:18px;color:#1a1a2e">${username}</h3>
<p style="margin:4px 0 0;font-size:13px;color:#64748b">${email}</p>
<span style="display:inline-block;margin-top:8px;padding:2px 10px;border-radius:10px;font-size:11px;font-weight:600;${
role === 'admin'
? 'background:#dbeafe;color:#1d4ed8'
: 'background:#f1f5f9;color:#64748b'
}">${role === 'admin' ? '🛡️ Admin' : '👤 ' + t('user_menu.role_user', 'User')}</span>
</div>
<div style="padding:16px 20px">
<div style="font-size:12px;color:#64748b;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">
<i class="fas fa-database" style="margin-right:4px"></i>${t('storage.title', 'Storage')}
</div>
<div style="background:#f1f5f9;border-radius:6px;height:8px;overflow:hidden;margin-bottom:4px">
<div style="height:100%;width:${percentage}%;background:${barColor};border-radius:6px;transition:width .3s"></div>
</div>
<div style="font-size:12px;color:#64748b;text-align:right">${percentage}% · ${formatFileSize(usedBytes)} / ${quotaBytes > 0 ? formatFileSize(quotaBytes) : '∞'}</div>
</div>
<div style="padding:0 20px 16px;display:flex;justify-content:center">
<button id="profile-modal-close" style="padding:8px 24px;border:1px solid #e2e8f0;border-radius:8px;background:#fff;color:#334155;font-size:13px;font-weight:600;cursor:pointer;transition:background .15s">${t('actions.close', 'Close')}</button>
</div>
</div>
`;
document.body.appendChild(overlay);
// Show with animation
requestAnimationFrame(() => overlay.classList.add('show'));
// Close handlers
overlay.querySelector('#profile-modal-close').addEventListener('click', () => {
overlay.classList.remove('show');
setTimeout(() => overlay.remove(), 200);
});
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
overlay.classList.remove('show');
setTimeout(() => overlay.remove(), 200);
}
});
}
/**
* Check if user is authenticated and load user's home folder
*/
+3 -3
View File
@@ -74,9 +74,9 @@ const ALL_LANGUAGES = [
{ code: 'es', name: 'Spanish', nativeName: 'Español', flag: '🇪🇸', popular: true },
{ code: 'zh', name: 'Chinese', nativeName: '中文', flag: '🇨🇳', popular: true },
{ code: 'fa', name: 'Persian', nativeName: 'فارسی', flag: '🇮🇷', popular: true },
{ code: 'fr', name: 'French', nativeName: 'Français', flag: '🇫🇷', popular: false },
{ code: 'de', name: 'German', nativeName: 'Deutsch', flag: '🇩🇪', popular: false },
{ code: 'pt', name: 'Portuguese', nativeName: 'Português', flag: '🇧🇷', popular: false },
{ code: 'fr', name: 'French', nativeName: 'Français', flag: '🇫🇷', popular: true },
{ 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: false },
{ code: 'ru', name: 'Russian', nativeName: 'Русский', flag: '🇷🇺', popular: false },
{ code: 'ja', name: 'Japanese', nativeName: '日本語', flag: '🇯🇵', popular: false },
+1 -1
View File
@@ -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'];
const supportedLocales = ['en', 'es', 'zh', 'fa', 'fr', 'de', 'pt'];
// Fallback to English if locale is not supported
if (!supportedLocales.includes(currentLocale)) {
+4 -1
View File
@@ -13,7 +13,10 @@ function getAvailableLanguages() {
{ code: 'en', name: 'English', flag: '🇬🇧' },
{ code: 'es', name: 'Español', flag: '🇪🇸' },
{ code: 'zh', name: '中文', flag: '🇨🇳' },
{ code: 'fa', name: 'فارسی', flag: '🇮🇷' }
{ code: 'fa', name: 'فارسی', flag: '🇮🇷' },
{ code: 'fr', name: 'Français', flag: '🇫🇷' },
{ code: 'de', name: 'Deutsch', flag: '🇩🇪' },
{ code: 'pt', name: 'Português', flag: '🇧🇷' }
];
}
+143 -137
View File
@@ -1,18 +1,18 @@
{
"app": {
"title": "OxiCloud",
"description": "Minimalistisches System zur Cloud-Datenspeicherung"
"description": "Minimalistisches Cloud-Speichersystem"
},
"nav": {
"files": "Dateien",
"shared": "Freigegebene Elemente",
"recent": "Zuletzt verwendete Elemente",
"shared": "Geteilt",
"recent": "Zuletzt verwendet",
"favorites": "Favoriten",
"trash": "Papierkorb"
},
"actions": {
"search": "Dateien durchsuchen...",
"new_folder": "Neuen Ordner erstellen",
"search": "Dateien suchen...",
"new_folder": "Neuer Ordner",
"upload": "Hochladen",
"upload_files": "Dateien hochladen",
"upload_folder": "Ordner hochladen",
@@ -24,13 +24,13 @@
"view": "Anzeigen",
"cancel": "Abbrechen",
"confirm": "Bestätigen",
"share": "Freigeben",
"share": "Teilen",
"favorite": "Zu Favoriten hinzufügen",
"unfavorite": "Aus Favoriten entfernen",
"copy": "Kopieren",
"notify": "Benachrichtigen",
"send": "Senden",
"clear_recent": "Verlauf löschen",
"clear_recent": "Zuletzt verwendete löschen",
"logout": "Abmelden",
"create": "Erstellen",
"search_btn": "Suchen",
@@ -39,55 +39,58 @@
"empty_trash": "Papierkorb leeren"
},
"user_menu": {
"appearance": "Darstellung",
"appearance": "Erscheinungsbild",
"about": "Über OxiCloud",
"about_description": "Plattform zur Cloud-Datenspeicherung, entwickelt mit Rust und Clean Architecture. Leistungsstark, sicher und datenschutzorientiert."
"about_description": "Cloud-Speicherplattform mit Rust und Clean Architecture. Schnell, sicher und privat.",
"admin_panel": "Admin-Panel",
"profile": "Mein Profil",
"role_user": "Benutzer"
},
"share": {
"dialogTitle": "Freigabelink erstellen",
"linkLabel": "Freigabelink:",
"dialogTitle": "Link teilen",
"linkLabel": "Geteilter Link:",
"copyLink": "Kopieren",
"permissions": "Berechtigungen:",
"permissionRead": "Leseberechtigung",
"permissionWrite": "Schreibberechtigung",
"permissionReshare": "Weiterfreigabeberechtigung",
"permissionRead": "Lesen",
"permissionWrite": "Schreiben",
"permissionReshare": "Weiterteilen",
"password": "Passwortschutz:",
"generatePassword": "Passwort generieren",
"generatePassword": "Generieren",
"expiration": "Ablaufdatum:",
"update": "Freigabe aktualisieren",
"remove": "Freigabe entfernen",
"notifyTitle": "Benachrichtigung versenden",
"notifyTitle": "Benachrichtigung senden",
"notifyEmailLabel": "E-Mail-Adresse:",
"notifyMessageLabel": "Nachricht (optional):",
"notifySend": "Benachrichtigung senden",
"shareWithOthers": "Mit weiteren Personen teilen",
"sharePublicly": "Öffentlich freigeben",
"shareWithOthers": "Mit anderen teilen",
"sharePublicly": "Öffentlich teilen",
"shareSettings": "Freigabeeinstellungen",
"shareCopied": "Der Link wurde in die Zwischenablage kopiert.",
"shareCreated": "Der Freigabelink wurde erfolgreich erstellt.",
"shareUpdated": "Die Freigabeeinstellungen wurden erfolgreich aktualisiert.",
"shareRemoved": "Die Freigabe wurde erfolgreich entfernt."
"shareCopied": "Link in Zwischenablage kopiert",
"shareCreated": "Freigabelink erfolgreich erstellt",
"shareUpdated": "Freigabeeinstellungen aktualisiert",
"shareRemoved": "Freigabe erfolgreich entfernt"
},
"share_dialogTitle": "Freigabelink erstellen",
"share_linkLabel": "Freigabelink:",
"share_dialogTitle": "Link teilen",
"share_linkLabel": "Geteilter Link:",
"share_copyLink": "Kopieren",
"share_permissions": "Berechtigungen:",
"share_permissionRead": "Leseberechtigung",
"share_permissionWrite": "Schreibberechtigung",
"share_permissionReshare": "Weiterfreigabeberechtigung",
"share_permissionRead": "Lesen",
"share_permissionWrite": "Schreiben",
"share_permissionReshare": "Weiterteilen",
"share_password": "Passwortschutz:",
"share_generatePassword": "Passwort generieren",
"share_generatePassword": "Generieren",
"share_expiration": "Ablaufdatum:",
"share_update": "Freigabe aktualisieren",
"share_remove": "Freigabe entfernen",
"share_notifyTitle": "Benachrichtigung versenden",
"share_notifyTitle": "Benachrichtigung senden",
"share_notifyEmailLabel": "E-Mail-Adresse:",
"share_notifyMessageLabel": "Nachricht (optional):",
"share_notifySend": "Benachrichtigung senden",
"shared": {
"backToFiles": "Zurück zur Dateiansicht",
"pageTitle": "Freigegebene Elemente",
"pageDescription": "Verwalten Sie Ihre freigegebenen Dateien und Ordner.",
"backToFiles": "Zurück zu Dateien",
"pageTitle": "Geteilte Ressourcen",
"pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner",
"filterType": "Typ:",
"filterAll": "Alle",
"filterFiles": "Dateien",
@@ -102,30 +105,31 @@
"colDateShared": "Freigabedatum",
"colExpiration": "Ablaufdatum",
"colPermissions": "Berechtigungen",
"colPassword": "Passwortschutz",
"colPassword": "Passwort",
"colActions": "Aktionen",
"emptyStateTitle": "Derzeit sind keine freigegebenen Elemente vorhanden.",
"emptyStateDesc": "Freigegebene Dateien und Ordner werden hier angezeigt.",
"goToFiles": "Zur Dateiansicht wechseln",
"emptyStateTitle": "Noch keine geteilten Ressourcen",
"emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt",
"goToFiles": "Zu Dateien gehen",
"typeFile": "Datei",
"typeFolder": "Ordner",
"noExpiration": "Kein Ablaufdatum festgelegt",
"noExpiration": "Kein Ablaufdatum",
"hasPassword": "Ja",
"noPassword": "Nein",
"editShare": "Freigabe bearbeiten",
"notifyShare": "Person benachrichtigen",
"notifyShare": "Jemanden benachrichtigen",
"copyLink": "Link kopieren",
"removeShare": "Freigabe entfernen",
"linkCopied": "Der Link wurde in die Zwischenablage kopiert.",
"linkCopyFailed": "Der Link konnte nicht kopiert werden.",
"itemUpdated": "Die Freigabeeinstellungen wurden erfolgreich aktualisiert.",
"itemRemoved": "Die Freigabe wurde erfolgreich entfernt.",
"invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
"notificationSent": "Die Benachrichtigung wurde erfolgreich versendet.",
"notificationFailed": "Die Benachrichtigung konnte nicht versendet werden.",
"shared_backToFiles": "Zurück zur Dateiansicht",
"shared_pageTitle": "Freigegebene Elemente",
"shared_pageDescription": "Verwalten Sie Ihre freigegebenen Dateien und Ordner.",
"linkCopied": "Link in Zwischenablage kopiert!",
"linkCopyFailed": "Link konnte nicht kopiert werden",
"itemUpdated": "Freigabeeinstellungen aktualisiert",
"itemRemoved": "Freigabe erfolgreich entfernt",
"invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
"notificationSent": "Benachrichtigung erfolgreich gesendet",
"notificationFailed": "Benachrichtigung konnte nicht gesendet werden"
},
"shared_backToFiles": "Zurück zu Dateien",
"shared_pageTitle": "Geteilte Ressourcen",
"shared_pageDescription": "Verwalten Sie Ihre geteilten Dateien und Ordner",
"shared_filterType": "Typ:",
"shared_filterAll": "Alle",
"shared_filterFiles": "Dateien",
@@ -140,34 +144,33 @@
"shared_colDateShared": "Freigabedatum",
"shared_colExpiration": "Ablaufdatum",
"shared_colPermissions": "Berechtigungen",
"shared_colPassword": "Passwortschutz",
"shared_colPassword": "Passwort",
"shared_colActions": "Aktionen",
"shared_emptyStateTitle": "Derzeit sind keine freigegebenen Elemente vorhanden.",
"shared_emptyStateDesc": "Freigegebene Dateien und Ordner werden hier angezeigt.",
"shared_goToFiles": "Zur Dateiansicht wechseln",
"shared_emptyStateTitle": "Noch keine geteilten Ressourcen",
"shared_emptyStateDesc": "Wenn Sie Dateien oder Ordner teilen, werden sie hier angezeigt",
"shared_goToFiles": "Zu Dateien gehen",
"shared_typeFile": "Datei",
"shared_typeFolder": "Ordner",
"shared_noExpiration": "Kein Ablaufdatum festgelegt",
"shared_noExpiration": "Kein Ablaufdatum",
"shared_hasPassword": "Ja",
"shared_noPassword": "Nein",
"shared_editShare": "Freigabe bearbeiten",
"shared_notifyShare": "Person benachrichtigen",
"shared_notifyShare": "Jemanden benachrichtigen",
"shared_copyLink": "Link kopieren",
"shared_removeShare": "Freigabe entfernen",
"shared_linkCopied": "Der Link wurde in die Zwischenablage kopiert.",
"shared_linkCopyFailed": "Der Link konnte nicht kopiert werden.",
"shared_itemUpdated": "Die Freigabeeinstellungen wurden erfolgreich aktualisiert.",
"shared_itemRemoved": "Die Freigabe wurde erfolgreich entfernt.",
"shared_invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
"shared_notificationSent": "Die Benachrichtigung wurde erfolgreich versendet.",
"shared_notificationFailed": "Die Benachrichtigung konnte nicht versendet werden."
},
"shared_linkCopied": "Link in Zwischenablage kopiert!",
"shared_linkCopyFailed": "Link konnte nicht kopiert werden",
"shared_itemUpdated": "Freigabeeinstellungen aktualisiert",
"shared_itemRemoved": "Freigabe erfolgreich entfernt",
"shared_invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
"shared_notificationSent": "Benachrichtigung erfolgreich gesendet",
"shared_notificationFailed": "Benachrichtigung konnte nicht gesendet werden",
"files": {
"name": "Name",
"type": "Typ",
"size": "Größe",
"modified": "Geändert am",
"no_files": "In diesem Ordner sind keine Dateien vorhanden.",
"modified": "Geändert",
"no_files": "Keine Dateien in diesem Ordner",
"loading": "Dateien werden geladen…",
"view_grid": "Rasteransicht",
"view_list": "Listenansicht",
@@ -176,8 +179,8 @@
"image": "Bild",
"video": "Video",
"audio": "Audio",
"pdf": "PDF-Dokument",
"text": "Textdokument",
"pdf": "PDF",
"text": "Text",
"folder": "Ordner"
}
},
@@ -185,27 +188,27 @@
"rename_folder": "Ordner umbenennen",
"rename_file": "Datei umbenennen",
"new_name": "Neuer Name",
"new_folder_title": "Neuen Ordner erstellen",
"new_folder_title": "Neuer Ordner",
"folder_name": "Ordnername",
"folder_placeholder": "Neuer Ordner",
"folder_placeholder": "Mein Ordner",
"rename_title": "Umbenennen",
"move_file": "Datei verschieben",
"move_folder": "Ordner verschieben",
"select_destination": "Zielordner auswählen:",
"root": "Hauptverzeichnis",
"delete_confirmation": "Sind Sie sicher, dass Sie dieses Element löschen möchten",
"and_contents": "und alle enthaltenen Elemente",
"no_undo": "Diese Aktion kann nicht rückgängig gemacht werden.",
"root": "Stammverzeichnis",
"delete_confirmation": "Sind Sie sicher, dass Sie löschen möchten",
"and_contents": "und den gesamten Inhalt",
"no_undo": "Diese Aktion kann nicht rückgängig gemacht werden",
"confirm_title": "Aktion bestätigen",
"confirm_delete": "In den Papierkorb verschieben",
"confirm_delete_file": "Möchten Sie die Datei \"{{name}}\" in den Papierkorb verschieben?",
"confirm_delete_folder": "Möchten Sie den Ordner \"{{name}}\" und alle enthaltenen Elemente in den Papierkorb verschieben?",
"confirm_delete": "In Papierkorb verschieben",
"confirm_delete_file": "Sind Sie sicher, dass Sie die Datei \"{{name}}\" in den Papierkorb verschieben möchten?",
"confirm_delete_folder": "Sind Sie sicher, dass Sie den Ordner \"{{name}}\" und seinen gesamten Inhalt in den Papierkorb verschieben möchten?",
"confirm_permanent_delete": "Endgültig löschen",
"confirm_permanent_delete_msg": "Möchten Sie dieses Element dauerhaft löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
"confirm_permanent_delete_msg": "Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
"confirm_empty_trash": "Papierkorb leeren",
"confirm_delete_share": "Freigabelink löschen",
"confirm_delete_share_msg": "Möchten Sie diesen Freigabelink löschen?",
"share_file": "Datei freigeben",
"confirm_delete_share_msg": "Sind Sie sicher, dass Sie diesen Freigabelink löschen möchten?",
"share_file": "Datei teilen",
"existing_shares": "Bestehende Freigaben",
"share_options": "Freigabeoptionen",
"password": "Passwort",
@@ -217,78 +220,78 @@
"message": "Nachricht"
},
"dropzone": {
"drag_files": "Ziehen Sie Dateien hierher oder klicken Sie, um Dateien auszuwählen.",
"drop_files": "Legen Sie Dateien hier ab, um den Upload zu starten."
"drag_files": "Dateien hierher ziehen oder klicken zum Auswählen",
"drop_files": "Dateien zum Hochladen ablegen"
},
"permissions": {
"read": "Leseberechtigung",
"write": "Schreibberechtigung",
"reshare": "Weiterfreigabeberechtigung"
"read": "Lesen",
"write": "Schreiben",
"reshare": "Weiterteilen"
},
"errors": {
"file_not_found": "Die Datei wurde nicht gefunden.",
"folder_not_found": "Der Ordner wurde nicht gefunden.",
"delete_error": "Beim Löschen ist ein Fehler aufgetreten.",
"upload_error": "Beim Hochladen ist ein Fehler aufgetreten.",
"rename_error": "Beim Umbenennen ist ein Fehler aufgetreten.",
"move_error": "Beim Verschieben ist ein Fehler aufgetreten.",
"empty_name": "Der Name darf nicht leer sein.",
"name_exists": "Eine Datei oder ein Ordner mit diesem Namen existiert bereits.",
"generic_error": "Es ist ein unerwarteter Fehler aufgetreten."
"file_not_found": "Datei nicht gefunden",
"folder_not_found": "Ordner nicht gefunden",
"delete_error": "Fehler beim Löschen",
"upload_error": "Fehler beim Hochladen",
"rename_error": "Fehler beim Umbenennen",
"move_error": "Fehler beim Verschieben",
"empty_name": "Der Name darf nicht leer sein",
"name_exists": "Eine Datei oder ein Ordner mit diesem Namen existiert bereits",
"generic_error": "Ein Fehler ist aufgetreten"
},
"breadcrumb": {
"home": "Startseite"
},
"trash": {
"empty_trash": "Papierkorb leeren",
"empty_state": "Der Papierkorb ist leer.",
"empty_state": "Der Papierkorb ist leer",
"original_location": "Ursprünglicher Speicherort",
"deleted_date": "Löschdatum",
"actions": "Aktionen",
"restore": "Wiederherstellen",
"delete_permanently": "Endgültig löschen",
"empty_confirm": "Sind Sie sicher, dass Sie den Papierkorb leeren möchten? Alle enthaltenen Elemente werden dauerhaft gelöscht."
"empty_confirm": "Sind Sie sicher, dass Sie den Papierkorb leeren möchten? Alle Elemente werden endgültig gelöscht."
},
"auth": {
"login_title": "Anmelden",
"username": "Benutzername",
"username_placeholder": "Bitte geben Sie Ihren Benutzernamen ein",
"username_placeholder": "Geben Sie Ihren Benutzernamen ein",
"password": "Passwort",
"password_placeholder": "Bitte geben Sie Ihr Passwort ein",
"password_placeholder": "Geben Sie Ihr Passwort ein",
"login_button": "Anmelden",
"no_account": "Verfügen Sie noch über kein Konto?",
"no_account": "Kein Konto?",
"register": "Registrieren",
"admin_setup": "Erstmalige Nutzung?",
"admin_setup": "Erstmalig?",
"setup": "Administrator einrichten",
"register_title": "Konto erstellen",
"email": "E-Mail-Adresse",
"email_placeholder": "Bitte geben Sie Ihre E-Mail-Adresse ein",
"email": "E-Mail",
"email_placeholder": "Geben Sie Ihre E-Mail ein",
"confirm_password": "Passwort bestätigen",
"confirm_password_placeholder": "Bitte bestätigen Sie Ihr Passwort",
"confirm_password_placeholder": "Bestätigen Sie Ihr Passwort",
"register_button": "Konto erstellen",
"have_account": "Verfügen Sie bereits über ein Konto?",
"have_account": "Bereits ein Konto?",
"login": "Anmelden",
"setup_title": "Ersteinrichtung",
"setup_step1": "Administrator",
"setup_step2": "Systemkonfiguration",
"setup_step3": "Abschluss",
"admin_username": "Administrator-Benutzername",
"admin_email": "Administrator-E-Mail-Adresse",
"admin_password": "Administrator-Passwort",
"setup_step1": "Admin",
"setup_step2": "System",
"setup_step3": "Abgeschlossen",
"admin_username": "Admin-Benutzername",
"admin_email": "Admin-E-Mail",
"admin_password": "Admin-Passwort",
"create_admin": "Administrator erstellen",
"back_to_login": "System bereits eingerichtet?",
"admin_success": "Das Administrator-Konto wurde erfolgreich erstellt. Sie können sich nun anmelden.",
"account_success": "Das Konto wurde erfolgreich erstellt. Sie können sich nun anmelden.",
"passwords_mismatch": "Die eingegebenen Passwörter stimmen nicht überein.",
"admin_create_error": "Beim Erstellen des Administrator-Kontos ist ein Fehler aufgetreten."
"back_to_login": "Bereits eingerichtet?",
"admin_success": "Administratorkonto erfolgreich erstellt! Sie können sich jetzt anmelden.",
"account_success": "Konto erfolgreich erstellt! Sie können sich jetzt anmelden.",
"passwords_mismatch": "Die Passwörter stimmen nicht überein",
"admin_create_error": "Fehler beim Erstellen des Administratorkontos"
},
"storage": {
"title": "Speicher",
"calculating": "Speicherplatz wird berechnet...",
"used": "{{percentage}} % verwendet ({{used}} von {{total}})"
"calculating": "Berechnung...",
"used": "{{percentage}}% verwendet ({{used}} / {{total}})"
},
"viewer": {
"unsupported_file": "Für diesen Dateityp ist keine Vorschau verfügbar.",
"unsupported_file": "Dieser Dateityp kann nicht in der Vorschau angezeigt werden.",
"download_file": "Datei herunterladen",
"zoom_in": "Vergrößern",
"zoom_out": "Verkleinern",
@@ -297,36 +300,39 @@
"language_selector": {
"title": "Willkommen bei OxiCloud",
"subtitle": "Bitte wählen Sie Ihre Sprache",
"continue": "Fortfahren",
"continue": "Weiter",
"languages": {
"en": "Englisch",
"es": "Spanisch",
"zh": "Chinesisch",
"fa": "Persisch"
"en": "English",
"es": "Español",
"zh": "中文",
"fa": "فارسی",
"fr": "Français",
"de": "Deutsch",
"pt": "Português"
}
},
"favorites": {
"empty_state": "Derzeit sind keine Favoriten vorhanden.",
"empty_hint": "Markieren Sie Dateien oder Ordner, um sie zu Ihren Favoriten hinzuzufügen.",
"empty_state": "Noch keine Favoriten",
"empty_hint": "Markieren Sie Dateien oder Ordner mit einem Stern, um sie zu Ihren Favoriten hinzuzufügen",
"add": "Zu Favoriten hinzufügen",
"remove": "Aus Favoriten entfernen"
},
"recent": {
"title": "Zuletzt verwendete Elemente",
"clear": "Verlauf löschen",
"accessed": "Geöffnet am",
"empty_state": "Keine zuletzt verwendeten Dateien vorhanden.",
"empty_hint": "Geöffnete Dateien werden hier angezeigt."
"title": "Zuletzt verwendet",
"clear": "Zuletzt verwendete löschen",
"accessed": "Zugegriffen",
"empty_state": "Keine zuletzt verwendeten Dateien",
"empty_hint": "Dateien, die Sie öffnen, werden hier angezeigt"
},
"notifications": {
"file_renamed": "Datei wurde umbenannt.",
"file_renamed_to": "Die Datei wurde in \"{{name}}\" umbenannt.",
"folder_renamed": "Ordner wurde umbenannt.",
"folder_renamed_to": "Der Ordner wurde in \"{{name}}\" umbenannt.",
"file_uploaded": "Die Datei wurde erfolgreich hochgeladen.",
"file_deleted": "Die Datei wurde in den Papierkorb verschoben.",
"folder_deleted": "Der Ordner wurde in den Papierkorb verschoben.",
"item_deleted_permanently": "Das Element wurde dauerhaft gelöscht.",
"trash_emptied": "Der Papierkorb wurde erfolgreich geleert."
"file_renamed": "Datei umbenannt",
"file_renamed_to": "Datei umbenannt in \"{{name}}\"",
"folder_renamed": "Ordner umbenannt",
"folder_renamed_to": "Ordner umbenannt in \"{{name}}\"",
"file_uploaded": "Datei hochgeladen",
"file_deleted": "Datei in Papierkorb verschoben",
"folder_deleted": "Ordner in Papierkorb verschoben",
"item_deleted_permanently": "Element endgültig gelöscht",
"trash_emptied": "Papierkorb erfolgreich geleert"
}
}
+8 -2
View File
@@ -41,7 +41,10 @@
"user_menu": {
"appearance": "Appearance",
"about": "About OxiCloud",
"about_description": "Cloud storage platform built with Rust & Clean Architecture. Fast, secure, and private."
"about_description": "Cloud storage platform built with Rust & Clean Architecture. Fast, secure, and private.",
"admin_panel": "Admin Panel",
"profile": "My Profile",
"role_user": "User"
},
"share": {
"dialogTitle": "Share Link",
@@ -302,7 +305,10 @@
"en": "English",
"es": "Español",
"zh": "中文",
"fa": "فارسی"
"fa": "فارسی",
"fr": "Français",
"de": "Deutsch",
"pt": "Português"
}
},
"favorites": {
+8 -2
View File
@@ -160,7 +160,10 @@
"user_menu": {
"appearance": "Apariencia",
"about": "Acerca de OxiCloud",
"about_description": "Plataforma de almacenamiento en la nube creada con Rust y Arquitectura Limpia. Rápida, segura y privada."
"about_description": "Plataforma de almacenamiento en la nube creada con Rust y Arquitectura Limpia. Rápida, segura y privada.",
"admin_panel": "Panel de administración",
"profile": "Mi perfil",
"role_user": "Usuario"
},
"files": {
"name": "Nombre",
@@ -302,7 +305,10 @@
"en": "English",
"es": "Español",
"zh": "中文",
"fa": "فارسی"
"fa": "فارسی",
"fr": "Français",
"de": "Deutsch",
"pt": "Português"
}
},
"favorites": {
+8 -2
View File
@@ -39,7 +39,10 @@
"user_menu": {
"appearance": "ظاهر",
"about": "درباره OxiCloud",
"about_description": "پلتفرم ذخیره‌سازی ابری ساخته شده با Rust و معماری تمیز. سریع، امن و خصوصی."
"about_description": "پلتفرم ذخیره‌سازی ابری ساخته شده با Rust و معماری تمیز. سریع، امن و خصوصی.",
"admin_panel": "پنل مدیریت",
"profile": "نمایه من",
"role_user": "کاربر"
},
"share": {
"dialogTitle": "پیوند هم‌رسانی",
@@ -288,7 +291,10 @@
"en": "English",
"es": "Español",
"zh": "中文",
"fa": "فارسی"
"fa": "فارسی",
"fr": "Français",
"de": "Deutsch",
"pt": "Português"
}
},
"favorites": {
+338
View File
@@ -0,0 +1,338 @@
{
"app": {
"title": "OxiCloud",
"description": "Système de stockage cloud minimaliste"
},
"nav": {
"files": "Fichiers",
"shared": "Partagés",
"recent": "Récents",
"favorites": "Favoris",
"trash": "Corbeille"
},
"actions": {
"search": "Rechercher des fichiers...",
"new_folder": "Nouveau dossier",
"upload": "Téléverser",
"upload_files": "Téléverser des fichiers",
"upload_folder": "Téléverser un dossier",
"rename": "Renommer",
"move": "Déplacer vers...",
"move_to": "Déplacer vers",
"delete": "Supprimer",
"download": "Télécharger",
"view": "Afficher",
"cancel": "Annuler",
"confirm": "Confirmer",
"share": "Partager",
"favorite": "Ajouter aux favoris",
"unfavorite": "Retirer des favoris",
"copy": "Copier",
"notify": "Notifier",
"send": "Envoyer",
"clear_recent": "Effacer les récents",
"logout": "Se déconnecter",
"create": "Créer",
"search_btn": "Rechercher",
"close": "Fermer",
"delete_permanently": "Supprimer définitivement",
"empty_trash": "Vider la corbeille"
},
"user_menu": {
"appearance": "Apparence",
"about": "À propos d'OxiCloud",
"about_description": "Plateforme de stockage cloud construite avec Rust et Architecture Propre. Rapide, sécurisée et privée.",
"admin_panel": "Panneau d'administration",
"profile": "Mon profil",
"role_user": "Utilisateur"
},
"share": {
"dialogTitle": "Lien de partage",
"linkLabel": "Lien partagé :",
"copyLink": "Copier",
"permissions": "Permissions :",
"permissionRead": "Lecture",
"permissionWrite": "Écriture",
"permissionReshare": "Repartager",
"password": "Protection par mot de passe :",
"generatePassword": "Générer",
"expiration": "Date d'expiration :",
"update": "Mettre à jour le partage",
"remove": "Supprimer le partage",
"notifyTitle": "Envoyer une notification",
"notifyEmailLabel": "Adresse e-mail :",
"notifyMessageLabel": "Message (facultatif) :",
"notifySend": "Envoyer la notification",
"shareWithOthers": "Partager avec d'autres",
"sharePublicly": "Partager publiquement",
"shareSettings": "Paramètres de partage",
"shareCopied": "Lien copié dans le presse-papiers",
"shareCreated": "Lien de partage créé avec succès",
"shareUpdated": "Paramètres de partage mis à jour",
"shareRemoved": "Partage supprimé avec succès"
},
"share_dialogTitle": "Lien de partage",
"share_linkLabel": "Lien partagé :",
"share_copyLink": "Copier",
"share_permissions": "Permissions :",
"share_permissionRead": "Lecture",
"share_permissionWrite": "Écriture",
"share_permissionReshare": "Repartager",
"share_password": "Protection par mot de passe :",
"share_generatePassword": "Générer",
"share_expiration": "Date d'expiration :",
"share_update": "Mettre à jour le partage",
"share_remove": "Supprimer le partage",
"share_notifyTitle": "Envoyer une notification",
"share_notifyEmailLabel": "Adresse e-mail :",
"share_notifyMessageLabel": "Message (facultatif) :",
"share_notifySend": "Envoyer la notification",
"shared": {
"backToFiles": "Retour aux fichiers",
"pageTitle": "Ressources partagées",
"pageDescription": "Gérez vos fichiers et dossiers partagés",
"filterType": "Type :",
"filterAll": "Tous",
"filterFiles": "Fichiers",
"filterFolders": "Dossiers",
"sortBy": "Trier par :",
"sortByName": "Nom",
"sortByDate": "Date de partage",
"sortByExpiration": "Expiration",
"search": "Rechercher",
"colName": "Nom",
"colType": "Type",
"colDateShared": "Date de partage",
"colExpiration": "Expiration",
"colPermissions": "Permissions",
"colPassword": "Mot de passe",
"colActions": "Actions",
"emptyStateTitle": "Aucune ressource partagée",
"emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici",
"goToFiles": "Aller aux fichiers",
"typeFile": "Fichier",
"typeFolder": "Dossier",
"noExpiration": "Sans expiration",
"hasPassword": "Oui",
"noPassword": "Non",
"editShare": "Modifier le partage",
"notifyShare": "Notifier quelqu'un",
"copyLink": "Copier le lien",
"removeShare": "Supprimer le partage",
"linkCopied": "Lien copié dans le presse-papiers !",
"linkCopyFailed": "Erreur lors de la copie du lien",
"itemUpdated": "Paramètres de partage mis à jour",
"itemRemoved": "Partage supprimé avec succès",
"invalidEmail": "Veuillez entrer une adresse e-mail valide",
"notificationSent": "Notification envoyée avec succès",
"notificationFailed": "Erreur lors de l'envoi de la notification"
},
"shared_backToFiles": "Retour aux fichiers",
"shared_pageTitle": "Ressources partagées",
"shared_pageDescription": "Gérez vos fichiers et dossiers partagés",
"shared_filterType": "Type :",
"shared_filterAll": "Tous",
"shared_filterFiles": "Fichiers",
"shared_filterFolders": "Dossiers",
"shared_sortBy": "Trier par :",
"shared_sortByName": "Nom",
"shared_sortByDate": "Date de partage",
"shared_sortByExpiration": "Expiration",
"shared_search": "Rechercher",
"shared_colName": "Nom",
"shared_colType": "Type",
"shared_colDateShared": "Date de partage",
"shared_colExpiration": "Expiration",
"shared_colPermissions": "Permissions",
"shared_colPassword": "Mot de passe",
"shared_colActions": "Actions",
"shared_emptyStateTitle": "Aucune ressource partagée",
"shared_emptyStateDesc": "Lorsque vous partagerez des fichiers ou dossiers, ils apparaîtront ici",
"shared_goToFiles": "Aller aux fichiers",
"shared_typeFile": "Fichier",
"shared_typeFolder": "Dossier",
"shared_noExpiration": "Sans expiration",
"shared_hasPassword": "Oui",
"shared_noPassword": "Non",
"shared_editShare": "Modifier le partage",
"shared_notifyShare": "Notifier quelqu'un",
"shared_copyLink": "Copier le lien",
"shared_removeShare": "Supprimer le partage",
"shared_linkCopied": "Lien copié dans le presse-papiers !",
"shared_linkCopyFailed": "Erreur lors de la copie du lien",
"shared_itemUpdated": "Paramètres de partage mis à jour",
"shared_itemRemoved": "Partage supprimé avec succès",
"shared_invalidEmail": "Veuillez entrer une adresse e-mail valide",
"shared_notificationSent": "Notification envoyée avec succès",
"shared_notificationFailed": "Erreur lors de l'envoi de la notification",
"files": {
"name": "Nom",
"type": "Type",
"size": "Taille",
"modified": "Modifié",
"no_files": "Aucun fichier dans ce dossier",
"loading": "Chargement des fichiers…",
"view_grid": "Vue en grille",
"view_list": "Vue en liste",
"file_types": {
"document": "Document",
"image": "Image",
"video": "Vidéo",
"audio": "Audio",
"pdf": "PDF",
"text": "Texte",
"folder": "Dossier"
}
},
"dialogs": {
"rename_folder": "Renommer le dossier",
"rename_file": "Renommer le fichier",
"new_name": "Nouveau nom",
"new_folder_title": "Nouveau dossier",
"folder_name": "Nom du dossier",
"folder_placeholder": "Mon dossier",
"rename_title": "Renommer",
"move_file": "Déplacer le fichier",
"move_folder": "Déplacer le dossier",
"select_destination": "Sélectionnez le dossier de destination :",
"root": "Racine",
"delete_confirmation": "Êtes-vous sûr de vouloir supprimer",
"and_contents": "et tout son contenu",
"no_undo": "Cette action est irréversible",
"confirm_title": "Confirmer l'action",
"confirm_delete": "Déplacer vers la corbeille",
"confirm_delete_file": "Êtes-vous sûr de vouloir déplacer le fichier « {{name}} » vers la corbeille ?",
"confirm_delete_folder": "Êtes-vous sûr de vouloir déplacer le dossier « {{name}} » et tout son contenu vers la corbeille ?",
"confirm_permanent_delete": "Supprimer définitivement",
"confirm_permanent_delete_msg": "Êtes-vous sûr de vouloir supprimer définitivement cet élément ? Cette action est irréversible.",
"confirm_empty_trash": "Vider la corbeille",
"confirm_delete_share": "Supprimer le lien de partage",
"confirm_delete_share_msg": "Êtes-vous sûr de vouloir supprimer ce lien de partage ?",
"share_file": "Partager le fichier",
"existing_shares": "Partages existants",
"share_options": "Options de partage",
"password": "Mot de passe",
"expiration": "Expiration",
"permissions": "Permissions",
"generated_link": "Lien généré",
"notify": "Envoyer une notification",
"recipient": "Destinataire",
"message": "Message"
},
"dropzone": {
"drag_files": "Glissez des fichiers ici ou cliquez pour sélectionner",
"drop_files": "Déposez les fichiers pour téléverser"
},
"permissions": {
"read": "Lecture",
"write": "Écriture",
"reshare": "Repartager"
},
"errors": {
"file_not_found": "Fichier introuvable",
"folder_not_found": "Dossier introuvable",
"delete_error": "Erreur lors de la suppression",
"upload_error": "Erreur lors du téléversement",
"rename_error": "Erreur lors du renommage",
"move_error": "Erreur lors du déplacement",
"empty_name": "Le nom ne peut pas être vide",
"name_exists": "Un fichier ou dossier portant ce nom existe déjà",
"generic_error": "Une erreur est survenue"
},
"breadcrumb": {
"home": "Accueil"
},
"trash": {
"empty_trash": "Vider la corbeille",
"empty_state": "La corbeille est vide",
"original_location": "Emplacement d'origine",
"deleted_date": "Date de suppression",
"actions": "Actions",
"restore": "Restaurer",
"delete_permanently": "Supprimer définitivement",
"empty_confirm": "Êtes-vous sûr de vouloir vider la corbeille ? Tous les éléments seront définitivement supprimés."
},
"auth": {
"login_title": "Se connecter",
"username": "Nom d'utilisateur",
"username_placeholder": "Entrez votre nom d'utilisateur",
"password": "Mot de passe",
"password_placeholder": "Entrez votre mot de passe",
"login_button": "Se connecter",
"no_account": "Vous n'avez pas de compte ?",
"register": "S'inscrire",
"admin_setup": "Première fois ?",
"setup": "Configurer l'administrateur",
"register_title": "Créer un compte",
"email": "E-mail",
"email_placeholder": "Entrez votre e-mail",
"confirm_password": "Confirmer le mot de passe",
"confirm_password_placeholder": "Confirmez votre mot de passe",
"register_button": "Créer un compte",
"have_account": "Vous avez déjà un compte ?",
"login": "Se connecter",
"setup_title": "Configuration initiale",
"setup_step1": "Admin",
"setup_step2": "Système",
"setup_step3": "Terminé",
"admin_username": "Nom d'utilisateur administrateur",
"admin_email": "E-mail administrateur",
"admin_password": "Mot de passe administrateur",
"create_admin": "Créer l'administrateur",
"back_to_login": "Déjà configuré ?",
"admin_success": "Compte administrateur créé avec succès ! Vous pouvez maintenant vous connecter.",
"account_success": "Compte créé avec succès ! Vous pouvez maintenant vous connecter.",
"passwords_mismatch": "Les mots de passe ne correspondent pas",
"admin_create_error": "Erreur lors de la création du compte administrateur"
},
"storage": {
"title": "Stockage",
"calculating": "Calcul en cours...",
"used": "{{percentage}}% utilisé ({{used}} / {{total}})"
},
"viewer": {
"unsupported_file": "Ce type de fichier ne peut pas être prévisualisé.",
"download_file": "Télécharger le fichier",
"zoom_in": "Zoom avant",
"zoom_out": "Zoom arrière",
"zoom_reset": "Réinitialiser le zoom"
},
"language_selector": {
"title": "Bienvenue sur OxiCloud",
"subtitle": "Veuillez sélectionner votre langue",
"continue": "Continuer",
"languages": {
"en": "English",
"es": "Español",
"zh": "中文",
"fa": "فارسی",
"fr": "Français",
"de": "Deutsch",
"pt": "Português"
}
},
"favorites": {
"empty_state": "Aucun favori pour le moment",
"empty_hint": "Marquez des fichiers ou dossiers avec une étoile pour les ajouter à vos favoris",
"add": "Ajouter aux favoris",
"remove": "Retirer des favoris"
},
"recent": {
"title": "Récents",
"clear": "Effacer les récents",
"accessed": "Consulté",
"empty_state": "Aucun fichier récent",
"empty_hint": "Les fichiers que vous ouvrez apparaîtront ici"
},
"notifications": {
"file_renamed": "Fichier renommé",
"file_renamed_to": "Fichier renommé en « {{name}} »",
"folder_renamed": "Dossier renommé",
"folder_renamed_to": "Dossier renommé en « {{name}} »",
"file_uploaded": "Fichier téléversé",
"file_deleted": "Fichier déplacé vers la corbeille",
"folder_deleted": "Dossier déplacé vers la corbeille",
"item_deleted_permanently": "Élément supprimé définitivement",
"trash_emptied": "Corbeille vidée avec succès"
}
}
+338
View File
@@ -0,0 +1,338 @@
{
"app": {
"title": "OxiCloud",
"description": "Sistema de armazenamento em nuvem minimalista"
},
"nav": {
"files": "Arquivos",
"shared": "Compartilhados",
"recent": "Recentes",
"favorites": "Favoritos",
"trash": "Lixeira"
},
"actions": {
"search": "Pesquisar arquivos...",
"new_folder": "Nova pasta",
"upload": "Enviar",
"upload_files": "Enviar arquivos",
"upload_folder": "Enviar pasta",
"rename": "Renomear",
"move": "Mover para...",
"move_to": "Mover para",
"delete": "Excluir",
"download": "Baixar",
"view": "Visualizar",
"cancel": "Cancelar",
"confirm": "Confirmar",
"share": "Compartilhar",
"favorite": "Adicionar aos favoritos",
"unfavorite": "Remover dos favoritos",
"copy": "Copiar",
"notify": "Notificar",
"send": "Enviar",
"clear_recent": "Limpar recentes",
"logout": "Sair",
"create": "Criar",
"search_btn": "Pesquisar",
"close": "Fechar",
"delete_permanently": "Excluir permanentemente",
"empty_trash": "Esvaziar lixeira"
},
"user_menu": {
"appearance": "Aparência",
"about": "Sobre o OxiCloud",
"about_description": "Plataforma de armazenamento em nuvem construída com Rust e Arquitetura Limpa. Rápida, segura e privada.",
"admin_panel": "Painel de administração",
"profile": "Meu perfil",
"role_user": "Usuário"
},
"share": {
"dialogTitle": "Link de compartilhamento",
"linkLabel": "Link compartilhado:",
"copyLink": "Copiar",
"permissions": "Permissões:",
"permissionRead": "Leitura",
"permissionWrite": "Escrita",
"permissionReshare": "Recompartilhar",
"password": "Proteção por senha:",
"generatePassword": "Gerar",
"expiration": "Data de expiração:",
"update": "Atualizar compartilhamento",
"remove": "Remover compartilhamento",
"notifyTitle": "Enviar notificação",
"notifyEmailLabel": "Endereço de e-mail:",
"notifyMessageLabel": "Mensagem (opcional):",
"notifySend": "Enviar notificação",
"shareWithOthers": "Compartilhar com outros",
"sharePublicly": "Compartilhar publicamente",
"shareSettings": "Configurações de compartilhamento",
"shareCopied": "Link copiado para a área de transferência",
"shareCreated": "Link de compartilhamento criado com sucesso",
"shareUpdated": "Configurações de compartilhamento atualizadas",
"shareRemoved": "Compartilhamento removido com sucesso"
},
"share_dialogTitle": "Link de compartilhamento",
"share_linkLabel": "Link compartilhado:",
"share_copyLink": "Copiar",
"share_permissions": "Permissões:",
"share_permissionRead": "Leitura",
"share_permissionWrite": "Escrita",
"share_permissionReshare": "Recompartilhar",
"share_password": "Proteção por senha:",
"share_generatePassword": "Gerar",
"share_expiration": "Data de expiração:",
"share_update": "Atualizar compartilhamento",
"share_remove": "Remover compartilhamento",
"share_notifyTitle": "Enviar notificação",
"share_notifyEmailLabel": "Endereço de e-mail:",
"share_notifyMessageLabel": "Mensagem (opcional):",
"share_notifySend": "Enviar notificação",
"shared": {
"backToFiles": "Voltar aos arquivos",
"pageTitle": "Recursos compartilhados",
"pageDescription": "Gerencie seus arquivos e pastas compartilhados",
"filterType": "Tipo:",
"filterAll": "Todos",
"filterFiles": "Arquivos",
"filterFolders": "Pastas",
"sortBy": "Ordenar por:",
"sortByName": "Nome",
"sortByDate": "Data de compartilhamento",
"sortByExpiration": "Expiração",
"search": "Pesquisar",
"colName": "Nome",
"colType": "Tipo",
"colDateShared": "Data de compartilhamento",
"colExpiration": "Expiração",
"colPermissions": "Permissões",
"colPassword": "Senha",
"colActions": "Ações",
"emptyStateTitle": "Nenhum recurso compartilhado ainda",
"emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui",
"goToFiles": "Ir para arquivos",
"typeFile": "Arquivo",
"typeFolder": "Pasta",
"noExpiration": "Sem expiração",
"hasPassword": "Sim",
"noPassword": "Não",
"editShare": "Editar compartilhamento",
"notifyShare": "Notificar alguém",
"copyLink": "Copiar link",
"removeShare": "Remover compartilhamento",
"linkCopied": "Link copiado para a área de transferência!",
"linkCopyFailed": "Falha ao copiar o link",
"itemUpdated": "Configurações de compartilhamento atualizadas",
"itemRemoved": "Compartilhamento removido com sucesso",
"invalidEmail": "Por favor, insira um endereço de e-mail válido",
"notificationSent": "Notificação enviada com sucesso",
"notificationFailed": "Falha ao enviar a notificação"
},
"shared_backToFiles": "Voltar aos arquivos",
"shared_pageTitle": "Recursos compartilhados",
"shared_pageDescription": "Gerencie seus arquivos e pastas compartilhados",
"shared_filterType": "Tipo:",
"shared_filterAll": "Todos",
"shared_filterFiles": "Arquivos",
"shared_filterFolders": "Pastas",
"shared_sortBy": "Ordenar por:",
"shared_sortByName": "Nome",
"shared_sortByDate": "Data de compartilhamento",
"shared_sortByExpiration": "Expiração",
"shared_search": "Pesquisar",
"shared_colName": "Nome",
"shared_colType": "Tipo",
"shared_colDateShared": "Data de compartilhamento",
"shared_colExpiration": "Expiração",
"shared_colPermissions": "Permissões",
"shared_colPassword": "Senha",
"shared_colActions": "Ações",
"shared_emptyStateTitle": "Nenhum recurso compartilhado ainda",
"shared_emptyStateDesc": "Quando você compartilhar arquivos ou pastas, eles aparecerão aqui",
"shared_goToFiles": "Ir para arquivos",
"shared_typeFile": "Arquivo",
"shared_typeFolder": "Pasta",
"shared_noExpiration": "Sem expiração",
"shared_hasPassword": "Sim",
"shared_noPassword": "Não",
"shared_editShare": "Editar compartilhamento",
"shared_notifyShare": "Notificar alguém",
"shared_copyLink": "Copiar link",
"shared_removeShare": "Remover compartilhamento",
"shared_linkCopied": "Link copiado para a área de transferência!",
"shared_linkCopyFailed": "Falha ao copiar o link",
"shared_itemUpdated": "Configurações de compartilhamento atualizadas",
"shared_itemRemoved": "Compartilhamento removido com sucesso",
"shared_invalidEmail": "Por favor, insira um endereço de e-mail válido",
"shared_notificationSent": "Notificação enviada com sucesso",
"shared_notificationFailed": "Falha ao enviar a notificação",
"files": {
"name": "Nome",
"type": "Tipo",
"size": "Tamanho",
"modified": "Modificado",
"no_files": "Nenhum arquivo nesta pasta",
"loading": "Carregando arquivos…",
"view_grid": "Visualização em grade",
"view_list": "Visualização em lista",
"file_types": {
"document": "Documento",
"image": "Imagem",
"video": "Vídeo",
"audio": "Áudio",
"pdf": "PDF",
"text": "Texto",
"folder": "Pasta"
}
},
"dialogs": {
"rename_folder": "Renomear pasta",
"rename_file": "Renomear arquivo",
"new_name": "Novo nome",
"new_folder_title": "Nova pasta",
"folder_name": "Nome da pasta",
"folder_placeholder": "Minha pasta",
"rename_title": "Renomear",
"move_file": "Mover arquivo",
"move_folder": "Mover pasta",
"select_destination": "Selecione a pasta de destino:",
"root": "Raiz",
"delete_confirmation": "Tem certeza de que deseja excluir",
"and_contents": "e todo o seu conteúdo",
"no_undo": "Esta ação não pode ser desfeita",
"confirm_title": "Confirmar ação",
"confirm_delete": "Mover para a lixeira",
"confirm_delete_file": "Tem certeza de que deseja mover o arquivo \"{{name}}\" para a lixeira?",
"confirm_delete_folder": "Tem certeza de que deseja mover a pasta \"{{name}}\" e todo o seu conteúdo para a lixeira?",
"confirm_permanent_delete": "Excluir permanentemente",
"confirm_permanent_delete_msg": "Tem certeza de que deseja excluir permanentemente este item? Esta ação não pode ser desfeita.",
"confirm_empty_trash": "Esvaziar lixeira",
"confirm_delete_share": "Excluir link de compartilhamento",
"confirm_delete_share_msg": "Tem certeza de que deseja excluir este link de compartilhamento?",
"share_file": "Compartilhar arquivo",
"existing_shares": "Compartilhamentos existentes",
"share_options": "Opções de compartilhamento",
"password": "Senha",
"expiration": "Expiração",
"permissions": "Permissões",
"generated_link": "Link gerado",
"notify": "Enviar notificação",
"recipient": "Destinatário",
"message": "Mensagem"
},
"dropzone": {
"drag_files": "Arraste arquivos aqui ou clique para selecionar",
"drop_files": "Solte os arquivos para enviar"
},
"permissions": {
"read": "Leitura",
"write": "Escrita",
"reshare": "Recompartilhar"
},
"errors": {
"file_not_found": "Arquivo não encontrado",
"folder_not_found": "Pasta não encontrada",
"delete_error": "Erro ao excluir",
"upload_error": "Erro ao enviar o arquivo",
"rename_error": "Erro ao renomear",
"move_error": "Erro ao mover",
"empty_name": "O nome não pode estar vazio",
"name_exists": "Já existe um arquivo ou pasta com esse nome",
"generic_error": "Ocorreu um erro"
},
"breadcrumb": {
"home": "Início"
},
"trash": {
"empty_trash": "Esvaziar lixeira",
"empty_state": "A lixeira está vazia",
"original_location": "Local original",
"deleted_date": "Data de exclusão",
"actions": "Ações",
"restore": "Restaurar",
"delete_permanently": "Excluir permanentemente",
"empty_confirm": "Tem certeza de que deseja esvaziar a lixeira? Todos os itens serão excluídos permanentemente."
},
"auth": {
"login_title": "Entrar",
"username": "Usuário",
"username_placeholder": "Digite seu nome de usuário",
"password": "Senha",
"password_placeholder": "Digite sua senha",
"login_button": "Entrar",
"no_account": "Não tem uma conta?",
"register": "Cadastre-se",
"admin_setup": "Primeira vez?",
"setup": "Configurar administrador",
"register_title": "Criar conta",
"email": "E-mail",
"email_placeholder": "Digite seu e-mail",
"confirm_password": "Confirmar senha",
"confirm_password_placeholder": "Confirme sua senha",
"register_button": "Criar conta",
"have_account": "Já tem uma conta?",
"login": "Entrar",
"setup_title": "Configuração inicial",
"setup_step1": "Admin",
"setup_step2": "Sistema",
"setup_step3": "Concluído",
"admin_username": "Usuário administrador",
"admin_email": "E-mail do administrador",
"admin_password": "Senha do administrador",
"create_admin": "Criar administrador",
"back_to_login": "Já configurado?",
"admin_success": "Conta de administrador criada com sucesso! Agora você pode entrar.",
"account_success": "Conta criada com sucesso! Agora você pode entrar.",
"passwords_mismatch": "As senhas não coincidem",
"admin_create_error": "Erro ao criar conta de administrador"
},
"storage": {
"title": "Armazenamento",
"calculating": "Calculando...",
"used": "{{percentage}}% usado ({{used}} / {{total}})"
},
"viewer": {
"unsupported_file": "Este tipo de arquivo não pode ser visualizado.",
"download_file": "Baixar arquivo",
"zoom_in": "Ampliar",
"zoom_out": "Reduzir",
"zoom_reset": "Redefinir zoom"
},
"language_selector": {
"title": "Bem-vindo ao OxiCloud",
"subtitle": "Por favor, selecione seu idioma",
"continue": "Continuar",
"languages": {
"en": "English",
"es": "Español",
"zh": "中文",
"fa": "فارسی",
"fr": "Français",
"de": "Deutsch",
"pt": "Português"
}
},
"favorites": {
"empty_state": "Nenhum favorito ainda",
"empty_hint": "Marque arquivos ou pastas com estrela para adicioná-los aos seus favoritos",
"add": "Adicionar aos favoritos",
"remove": "Remover dos favoritos"
},
"recent": {
"title": "Recentes",
"clear": "Limpar recentes",
"accessed": "Acessado",
"empty_state": "Nenhum arquivo recente",
"empty_hint": "Os arquivos que você abrir aparecerão aqui"
},
"notifications": {
"file_renamed": "Arquivo renomeado",
"file_renamed_to": "Arquivo renomeado para \"{{name}}\"",
"folder_renamed": "Pasta renomeada",
"folder_renamed_to": "Pasta renomeada para \"{{name}}\"",
"file_uploaded": "Arquivo enviado",
"file_deleted": "Arquivo movido para a lixeira",
"folder_deleted": "Pasta movida para a lixeira",
"item_deleted_permanently": "Item excluído permanentemente",
"trash_emptied": "Lixeira esvaziada com sucesso"
}
}
+8 -2
View File
@@ -39,7 +39,10 @@
"user_menu": {
"appearance": "外观",
"about": "关于 OxiCloud",
"about_description": "基于 Rust 和整洁架构构建的云存储平台。快速、安全、私密。"
"about_description": "基于 Rust 和整洁架构构建的云存储平台。快速、安全、私密。",
"admin_panel": "管理面板",
"profile": "我的资料",
"role_user": "用户"
},
"share": {
"dialogTitle": "共享链接",
@@ -250,7 +253,10 @@
"en": "English",
"es": "Español",
"zh": "中文",
"fa": "فارسی"
"fa": "فارسی",
"fr": "Français",
"de": "Deutsch",
"pt": "Português"
}
},
"favorites": {