feat(static-assets): introduce StaticAssetHostService and enhance static asset handling and i18n

- Added StaticAssetHostService to manage static asset host resolution with caching.
- Updated StaticAssetService and StaticDashboardService to utilize the new static asset host resolver.
- Enhanced StaticWebController to pass request host information for improved asset handling.
- Refactored static asset interfaces to support new functionality.
- Integrated CORS headers and cache policies for better asset management.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-11-18 17:32:39 +08:00
parent c94a601f83
commit efa5ade0da
32 changed files with 2675 additions and 636 deletions

View File

@@ -182,16 +182,266 @@
"photos.conflict.metadata.label": "Metadata mismatch",
"photos.conflict.missing.description": "Record exists in the database but the storage object is no longer accessible.",
"photos.conflict.missing.label": "Missing in storage",
"photos.library.actions.all-selected": "All selected",
"photos.library.actions.clear-selection": "Clear selection",
"photos.library.actions.delete": "Delete",
"photos.library.actions.edit-tags": "Edit tags",
"photos.library.actions.select-all": "Select all",
"photos.library.actions.selected-count": "{{count}} selected",
"photos.library.actions.upload": "Upload files",
"photos.library.actions.upload-short": "Upload",
"photos.library.card.delete": "Delete asset",
"photos.library.card.device-unknown": "Unknown device",
"photos.library.card.edit-tags": "Edit tags",
"photos.library.card.no-preview": "Preview unavailable",
"photos.library.card.select": "Select",
"photos.library.card.selected": "Selected",
"photos.library.card.size-unknown": "Size unknown",
"photos.library.card.view-exif": "View EXIF",
"photos.library.delete.cancel": "Cancel",
"photos.library.delete.confirm": "Delete",
"photos.library.delete.description": "This action cannot be undone. Continue deleting “{{name}}”? Enable the option below to also remove the remote storage file.",
"photos.library.delete.option.description": "When checked, the remote originals and thumbnails will be removed as well.",
"photos.library.delete.option.title": "Also delete storage files",
"photos.library.delete.title": "Delete this asset?",
"photos.library.empty.description": "Use the upload button to add new photos to your library.",
"photos.library.empty.title": "No photos yet",
"photos.library.exif.empty": "No EXIF data is available for this asset.",
"photos.library.exif.file": "File: {{value}}",
"photos.library.exif.rows.altitude": "Altitude",
"photos.library.exif.rows.altitude-below": "{{value}} m (below sea level)",
"photos.library.exif.rows.altitude-value": "{{value}} m",
"photos.library.exif.rows.aperture": "Aperture",
"photos.library.exif.rows.aspect-ratio": "Aspect ratio",
"photos.library.exif.rows.author": "Author",
"photos.library.exif.rows.brightness": "Brightness value",
"photos.library.exif.rows.captured-at": "Captured at",
"photos.library.exif.rows.color-space": "Color space",
"photos.library.exif.rows.copyright": "Copyright",
"photos.library.exif.rows.device": "Camera",
"photos.library.exif.rows.eq-focal-length": "35mm equivalent focal length",
"photos.library.exif.rows.exposure-compensation": "Exposure compensation",
"photos.library.exif.rows.exposure-mode": "Exposure mode",
"photos.library.exif.rows.exposure-program": "Exposure program",
"photos.library.exif.rows.file-format": "Format",
"photos.library.exif.rows.file-size": "File size",
"photos.library.exif.rows.flash": "Flash",
"photos.library.exif.rows.focal-length": "Focal length",
"photos.library.exif.rows.iso": "ISO",
"photos.library.exif.rows.latitude": "Latitude",
"photos.library.exif.rows.lens": "Lens",
"photos.library.exif.rows.light-source": "Light source",
"photos.library.exif.rows.longitude": "Longitude",
"photos.library.exif.rows.megapixels": "Megapixels",
"photos.library.exif.rows.metering-mode": "Metering mode",
"photos.library.exif.rows.photo-id": "Photo ID",
"photos.library.exif.rows.rating": "Rating",
"photos.library.exif.rows.resolution": "Resolution",
"photos.library.exif.rows.scale-factor": "35mm scale factor",
"photos.library.exif.rows.scene-type": "Scene type",
"photos.library.exif.rows.sensor": "Sensor",
"photos.library.exif.rows.shutter": "Shutter speed",
"photos.library.exif.rows.software": "Software",
"photos.library.exif.rows.time-offset": "Time offset",
"photos.library.exif.rows.timezone": "Time zone",
"photos.library.exif.rows.timezone-source": "Time zone source",
"photos.library.exif.rows.title": "Title",
"photos.library.exif.rows.white-balance": "White balance",
"photos.library.exif.sections.basic": "Basic Info",
"photos.library.exif.sections.capture": "Capture Settings",
"photos.library.exif.sections.fuji": "Fujifilm Recipe",
"photos.library.exif.sections.location": "Location",
"photos.library.exif.sections.metadata": "Metadata",
"photos.library.sort.by-captured": "Sort by capture time",
"photos.library.sort.by-uploaded": "Sort by uploaded time",
"photos.library.sort.order-asc": "Oldest first",
"photos.library.sort.order-desc": "Newest first",
"photos.library.tags.modal.asset-count": "{{count}} assets",
"photos.library.tags.modal.cancel": "Cancel",
"photos.library.tags.modal.description.multiple": "All selected assets will receive the same tags. Tags also determine the remote storage path.",
"photos.library.tags.modal.description.single": "Tags determine the remote storage path. Moving tags will relocate original files and Live Photo videos accordingly.",
"photos.library.tags.modal.input": "Type a tag and press Enter, or select from suggested tags",
"photos.library.tags.modal.no-selection": "No asset selected",
"photos.library.tags.modal.path.hint": "Based on tag order",
"photos.library.tags.modal.path.preview": "New storage path preview",
"photos.library.tags.modal.path.sample": "Sample path (first item)",
"photos.library.tags.modal.save": "Save",
"photos.library.tags.modal.saving": "Saving…",
"photos.library.tags.modal.title": "Edit tags for “{{name}}”",
"photos.library.tags.toast.error": "Failed to update tags",
"photos.library.tags.toast.error-description": "Please try again later.",
"photos.library.tags.toast.multi-success": "Updated tags for {{count}} assets",
"photos.library.tags.toast.single-success": "Tags updated",
"photos.library.tags.toast.success-description": "Storage paths have been updated for the new tag structure.",
"photos.page.description": "Sync and manage photo assets on the server.",
"photos.page.title": "Photo Library",
"photos.sync.actions.button.apply": "Sync photos",
"photos.sync.actions.button.preview": "Preview sync",
"photos.sync.actions.toast.apply-success": "Photo sync completed",
"photos.sync.actions.toast.error-description": "Photo sync failed. Please try again later.",
"photos.sync.actions.toast.error-title": "Sync failed",
"photos.sync.actions.toast.preview-success": "Preview sync finished",
"photos.sync.actions.toast.success-description": "Inserted {{inserted}} · Updated {{updated}} · Conflicts {{conflicts}} · Errors {{errors}}",
"photos.sync.conflicts.actions.all-database": "Set all to database",
"photos.sync.conflicts.actions.all-storage": "Set all to storage",
"photos.sync.conflicts.actions.clear-selection": "Clear selection",
"photos.sync.conflicts.actions.hide-details": "Hide details",
"photos.sync.conflicts.actions.open-storage": "Open",
"photos.sync.conflicts.actions.prefer-database": "Keep database",
"photos.sync.conflicts.actions.prefer-storage": "Keep storage",
"photos.sync.conflicts.actions.selected-database": "Apply database to selected",
"photos.sync.conflicts.actions.selected-storage": "Apply storage to selected",
"photos.sync.conflicts.actions.view-details": "View details",
"photos.sync.conflicts.actions.view-original": "View original",
"photos.sync.conflicts.description": "These conflicts need manual confirmation. Select multiple entries to resolve faster.",
"photos.sync.conflicts.info.conflict-key": "Conflict key:",
"photos.sync.conflicts.info.first-detected": "Detected: {{time}}",
"photos.sync.conflicts.info.last-updated": "Last updated: {{time}}",
"photos.sync.conflicts.info.photo-id-fallback": "Photo ID missing",
"photos.sync.conflicts.info.storage-key": "Storage key:",
"photos.sync.conflicts.preview.common.dimensions": "Dimensions:",
"photos.sync.conflicts.preview.common.id": "ID:",
"photos.sync.conflicts.preview.common.size": "Size:",
"photos.sync.conflicts.preview.common.updated-at": "Updated:",
"photos.sync.conflicts.preview.database.empty": "No database record available",
"photos.sync.conflicts.preview.database.title": "Database record",
"photos.sync.conflicts.preview.storage.key": "Key:",
"photos.sync.conflicts.preview.storage.title": "Storage object",
"photos.sync.conflicts.prompts.bulk": "Resolve {{scope}} and {{strategy}}?",
"photos.sync.conflicts.prompts.cancel": "Cancel",
"photos.sync.conflicts.prompts.confirm": "Confirm",
"photos.sync.conflicts.prompts.scope-all": "all pending conflicts",
"photos.sync.conflicts.prompts.scope-selected": "the {{count}} selected conflicts",
"photos.sync.conflicts.prompts.single": "Resolve conflict {{identifier}} and {{strategy}}?",
"photos.sync.conflicts.prompts.title": "Confirm action",
"photos.sync.conflicts.selection.clear": "Clear selection",
"photos.sync.conflicts.selection.none": "No items selected",
"photos.sync.conflicts.selection.selected": "{{count}} selected",
"photos.sync.conflicts.strategy.database": "keep database version",
"photos.sync.conflicts.strategy.storage": "keep storage version",
"photos.sync.conflicts.title": "Pending conflicts",
"photos.sync.conflicts.toast.no-original": "No original asset preview is available for this record.",
"photos.sync.conflicts.toast.none": "No conflicts to resolve right now.",
"photos.sync.conflicts.toast.open-storage-failed": "Unable to open storage object",
"photos.sync.conflicts.toast.select-required": "Select conflicts first.",
"photos.sync.conflicts.total": "Total: {{count}}",
"photos.sync.metadata.database": "Metadata · database",
"photos.sync.metadata.etag": "ETag",
"photos.sync.metadata.hash": "Metadata hash",
"photos.sync.metadata.none": "None",
"photos.sync.metadata.size": "Size",
"photos.sync.metadata.storage": "Metadata · storage",
"photos.sync.metadata.unknown": "Unknown",
"photos.sync.metadata.updated-at": "Updated",
"photos.sync.progress.heading.error": "Sync failed",
"photos.sync.progress.heading.preview": "Running preview sync",
"photos.sync.progress.heading.running": "Running sync",
"photos.sync.progress.logs.detail.error": "Error {{value}}",
"photos.sync.progress.logs.detail.live-photo": "Includes Live Photo",
"photos.sync.progress.logs.detail.live-photo-absent": "No Live Photo",
"photos.sync.progress.logs.detail.manifest": "Includes previous manifest",
"photos.sync.progress.logs.detail.manifest-absent": "No previous manifest",
"photos.sync.progress.logs.detail.result": "Result {{value}}",
"photos.sync.progress.logs.level.error": "Error",
"photos.sync.progress.logs.level.info": "Info",
"photos.sync.progress.logs.level.success": "Success",
"photos.sync.progress.logs.level.warn": "Warning",
"photos.sync.progress.logs.recent": "Latest {{count}} entries",
"photos.sync.progress.logs.title": "Sync logs",
"photos.sync.progress.recent.no-further": "No further actions required",
"photos.sync.progress.recent.progress": "Progress: {{processed}} / {{total}}",
"photos.sync.progress.recent.title": "Recent action",
"photos.sync.progress.stage-status.completed": "Completed",
"photos.sync.progress.stage-status.pending": "Pending",
"photos.sync.progress.stage-status.running": "Running",
"photos.sync.progress.stages.conflicts.description": "Detect differences between storage metadata and database records.",
"photos.sync.progress.stages.conflicts.label": "Verify metadata",
"photos.sync.progress.stages.missing.description": "Sync new objects from storage into the database.",
"photos.sync.progress.stages.missing.label": "Import new photos",
"photos.sync.progress.stages.orphan.description": "Mark database entries whose storage objects are missing.",
"photos.sync.progress.stages.orphan.label": "Identify orphan records",
"photos.sync.progress.stages.progress": "{{processed}} / {{total}}",
"photos.sync.progress.stages.reconciliation.description": "Update record statuses to match the latest metadata.",
"photos.sync.progress.stages.reconciliation.label": "Reconcile statuses",
"photos.sync.progress.status.error": "Stopped",
"photos.sync.progress.status.running": "In progress",
"photos.sync.progress.subtitle.error": "An error occurred during sync. Review the log for details and retry.",
"photos.sync.progress.subtitle.preview": "Simulating sync operations. Preview mode does not write to the database.",
"photos.sync.progress.subtitle.running": "Reconciling storage with the database. Keep this page open to view live progress.",
"photos.sync.result.actions.applied": "Applied",
"photos.sync.result.actions.collapse": "Hide details",
"photos.sync.result.actions.expand": "View details",
"photos.sync.result.actions.pending": "Not applied",
"photos.sync.result.alerts.open-original-failed": "Unable to open original photo",
"photos.sync.result.duration.less-than-second": "Less than 1 second",
"photos.sync.result.duration.minutes": "{{count}} min",
"photos.sync.result.duration.seconds": "{{count}} sec",
"photos.sync.result.filters.all": "All",
"photos.sync.result.history.completed-at": "Completed {{time}}",
"photos.sync.result.history.duration": "Duration {{duration}}",
"photos.sync.result.history.heading": "Last sync completed",
"photos.sync.result.history.mode.live": "Live mode · Changes applied",
"photos.sync.result.history.mode.preview": "Preview mode · No database writes",
"photos.sync.result.history.operations": "Actions: {{count}}",
"photos.sync.result.info.conflict-type": "Conflict type:",
"photos.sync.result.info.photo-id": "Photo ID:",
"photos.sync.result.info.storage-key": "Storage key:",
"photos.sync.result.manifest.empty": "No manifest data",
"photos.sync.result.operations.count": "Operations: {{count}}",
"photos.sync.result.operations.filter-label": "· Filter:",
"photos.sync.result.status.empty.description": "Configure and activate a storage provider in Settings, then use the sync controls to start processing. Preview runs are safe and do not mutate data.",
"photos.sync.result.status.empty.title": "No sync runs yet",
"photos.sync.result.status.loading.description": "Fetching the latest sync run. Please wait…",
"photos.sync.result.status.loading.title": "Loading sync status",
"photos.sync.result.summary.description.latest": "Latest sync results are shown below.",
"photos.sync.result.summary.description.live": "The last sync ran in live mode and wrote changes to the database.",
"photos.sync.result.summary.description.preview": "Preview mode ran most recently; no database changes were applied.",
"photos.sync.result.summary.heading": "Sync summary",
"photos.sync.result.summary.labels.completed": "Synced",
"photos.sync.result.summary.labels.conflicts": "Conflicts",
"photos.sync.result.summary.labels.database-records": "Database records",
"photos.sync.result.summary.labels.deleted": "Records deleted",
"photos.sync.result.summary.labels.errors": "Errors",
"photos.sync.result.summary.labels.inserted": "Photos inserted",
"photos.sync.result.summary.labels.pending": "Pending",
"photos.sync.result.summary.labels.skipped": "Skipped",
"photos.sync.result.summary.labels.storage-objects": "Storage objects",
"photos.sync.result.summary.labels.updated": "Records updated",
"photos.sync.result.table.empty.filtered": "No actions match the current filter.",
"photos.sync.result.table.empty.none": "Sync finished with no actions to review.",
"photos.sync.result.table.mode.live": "Live mode · Results saved",
"photos.sync.result.table.mode.preview": "Preview mode · No changes applied",
"photos.sync.result.table.title": "Sync action details",
"photos.sync.toasts.conflict-batch-error": "Some conflicts failed to resolve",
"photos.sync.toasts.conflict-batch-success": "{{strategy}} resolved {{count}} conflict(s).",
"photos.sync.toasts.conflict-database": "Database record retained; storage difference ignored.",
"photos.sync.toasts.conflict-error": "Failed to resolve conflict",
"photos.sync.toasts.conflict-error-desc": "Unable to resolve conflict right now. Please try again later.",
"photos.sync.toasts.conflict-resolved": "Conflict resolved",
"photos.sync.toasts.conflict-select": "Select at least one conflict first.",
"photos.sync.toasts.conflict-storage": "Storage version applied to the database.",
"photos.tabs.library": "Library",
"photos.tabs.storage": "Storage",
"photos.tabs.sync": "Sync",
"photos.tabs.usage": "Usage",
"photos.usage.events.description": "Latest billing-relevant actions such as uploads, deletions, and sync jobs.",
"photos.usage.events.empty.description": "Upload photos or run sync to see billing events here.",
"photos.usage.events.empty.title": "No usage yet",
"photos.usage.events.metadata.empty": "—",
"photos.usage.events.metadata.more": "+{{count}} more",
"photos.usage.events.metadata.value-unknown": "N/A",
"photos.usage.events.title": "Recent usage events",
"photos.usage.events.total": "{{count}} event(s)",
"photos.usage.events.unit.byte": "Bytes",
"photos.usage.events.unit.count": "Actions",
"photos.usage.events.unit.label": "Unit: {{unit}}",
"photos.usage.photo-created.description": "Photos added through upload or sync.",
"photos.usage.photo-created.label": "Photo created",
"photos.usage.photo-deleted.description": "Photos removed from the library or storage.",
"photos.usage.photo-deleted.label": "Photo deleted",
"photos.usage.summary.description": "Cumulative totals grouped by event type.",
"photos.usage.summary.refresh": "Refresh",
"photos.usage.summary.title": "Usage overview",
"photos.usage.sync-completed.description": "Summary event when a data sync run finishes.",
"photos.usage.sync-completed.label": "Sync completed",
"plan.badge.current": "Current Plan",
@@ -234,6 +484,136 @@
"settings.site.title": "Site Settings",
"settings.user.description": "Maintain the public author profile, avatar, and aliases.",
"settings.user.title": "User Profile",
"site.settings.banner.dirty": "{{count}} setting(s) pending save",
"site.settings.banner.fail": "Save failed:",
"site.settings.banner.success": "Settings synced successfully",
"site.settings.banner.synced": "All settings are up to date",
"site.settings.button.save": "Save changes",
"site.settings.button.saving": "Saving…",
"site.settings.error.load-prefix": "Unable to load site settings:",
"site.settings.error.unknown": "Unknown error",
"site.user.blocker.cancel": "Stay on this page",
"site.user.blocker.confirm": "Leave anyway",
"site.user.blocker.description": "You have unsaved edits. Leaving now will discard them. Continue?",
"site.user.blocker.title": "Save changes before leaving",
"site.user.button.save": "Save changes",
"site.user.button.saving": "Saving…",
"site.user.error.loading": "Unable to load user profile",
"site.user.form.avatar.helper": "Supports http(s) URLs or links starting with //. Leave blank to fallback to initials.",
"site.user.form.avatar.label": "Avatar URL",
"site.user.form.avatar.placeholder": "https://cdn.example.com/avatar.png",
"site.user.form.display.helper": "Shown next to the avatar. Leave blank to reuse the author name.",
"site.user.form.display.label": "Display name",
"site.user.form.display.placeholder": "Optional, e.g., innei.photo",
"site.user.form.name.helper": "Displayed publicly and in RSS author/editor fields.",
"site.user.form.name.label": "Author name",
"site.user.form.name.placeholder": "e.g., Innei",
"site.user.form.username.helper": "Used internally to identify the author. Not shown on the site.",
"site.user.form.username.label": "Username (optional)",
"site.user.form.username.placeholder": "e.g., innei",
"site.user.header.badge": "Author profile",
"site.user.header.description": "Used on the site header, RSS feed, and social cards. Keep it aligned with your personal brand.",
"site.user.header.title": "Public author identity",
"site.user.preview.avatar-alt": "Author avatar preview",
"site.user.preview.fallback": "Author",
"site.user.preview.last-updated": "Last updated: {{time}}",
"site.user.preview.never-updated": "Not updated yet",
"site.user.toast.error": "Failed to save profile",
"site.user.toast.error-description": "Please review your input and try again.",
"site.user.toast.success": "Profile updated",
"storage.providers.actions.add": "Add provider",
"storage.providers.actions.cancel": "Cancel",
"storage.providers.actions.create": "Create provider",
"storage.providers.actions.save": "Save changes",
"storage.providers.actions.saving": "Saving…",
"storage.providers.blocker.cancel": "Stay on this page",
"storage.providers.blocker.confirm": "Leave anyway",
"storage.providers.blocker.description": "You have unsaved storage provider changes. Leaving this page will discard them. Continue?",
"storage.providers.blocker.title": "Save storage changes first",
"storage.providers.card.active": "Active",
"storage.providers.card.edit": "Edit",
"storage.providers.card.make-active": "Make active",
"storage.providers.card.make-inactive": "Make inactive",
"storage.providers.card.preview.fallback": "Storage provider",
"storage.providers.card.preview.not-configured": "Not configured",
"storage.providers.card.untitled": "Untitled provider",
"storage.providers.empty.action": "Add provider",
"storage.providers.empty.description": "Add a storage provider so the system can sync and manage remote photos.",
"storage.providers.empty.title": "No storage provider configured",
"storage.providers.error.load": "Unable to load storage settings",
"storage.providers.fields.github.branch.description": "Optional branch to sync.",
"storage.providers.fields.github.branch.helper": "Defaults to master/main. Provide the full branch name if it differs.",
"storage.providers.fields.github.branch.label": "Branch",
"storage.providers.fields.github.branch.placeholder": "main",
"storage.providers.fields.github.owner.description": "GitHub user or organization name.",
"storage.providers.fields.github.owner.label": "Repository owner",
"storage.providers.fields.github.owner.placeholder": "afilmory",
"storage.providers.fields.github.path.description": "Optional path within the repository to limit syncing.",
"storage.providers.fields.github.path.label": "Repository path",
"storage.providers.fields.github.path.placeholder": "public/photos",
"storage.providers.fields.github.repo.description": "Repository that stores your photos.",
"storage.providers.fields.github.repo.label": "Repository name",
"storage.providers.fields.github.repo.placeholder": "photo-assets",
"storage.providers.fields.github.token.description": "Personal Access Token for private repositories.",
"storage.providers.fields.github.token.label": "Access token",
"storage.providers.fields.github.token.placeholder": "ghp_xxxxxxxxxxxxxxxxxxxx",
"storage.providers.fields.github.use-raw.description": "Use raw.githubusercontent.com when generating public URLs.",
"storage.providers.fields.github.use-raw.helper": "Set to false if you serve files via a custom domain.",
"storage.providers.fields.github.use-raw.label": "Use raw URL",
"storage.providers.fields.github.use-raw.placeholder": "true / false",
"storage.providers.fields.s3.access-key.label": "Access Key ID",
"storage.providers.fields.s3.access-key.placeholder": "AKIAxxxxxxxxxxxx",
"storage.providers.fields.s3.bucket.description": "Name of the S3 bucket that stores your photos.",
"storage.providers.fields.s3.bucket.label": "Bucket name",
"storage.providers.fields.s3.bucket.placeholder": "afilmory-photos",
"storage.providers.fields.s3.custom-domain.description": "Domain used when generating public photo URLs.",
"storage.providers.fields.s3.custom-domain.label": "Custom public domain",
"storage.providers.fields.s3.custom-domain.placeholder": "https://cdn.example.com",
"storage.providers.fields.s3.endpoint.description": "Optional endpoint for S3-compatible services.",
"storage.providers.fields.s3.endpoint.helper": "Leave empty for AWS S3. Required for MinIO or other vendors.",
"storage.providers.fields.s3.endpoint.label": "Custom endpoint",
"storage.providers.fields.s3.endpoint.placeholder": "https://s3.example.com",
"storage.providers.fields.s3.exclude-regex.description": "Optional. Skip files that match this regular expression.",
"storage.providers.fields.s3.exclude-regex.helper": "Use JavaScript-compatible regular expressions.",
"storage.providers.fields.s3.exclude-regex.label": "Exclude pattern (regex)",
"storage.providers.fields.s3.exclude-regex.placeholder": "\\\\.(tmp|bak)$",
"storage.providers.fields.s3.max-files.description": "Optional limit for how many files to scan per run.",
"storage.providers.fields.s3.max-files.label": "Max files",
"storage.providers.fields.s3.max-files.placeholder": "1000",
"storage.providers.fields.s3.prefix.description": "Optional. Limit scanning to objects under this prefix.",
"storage.providers.fields.s3.prefix.label": "Path prefix",
"storage.providers.fields.s3.prefix.placeholder": "photos/",
"storage.providers.fields.s3.region.description": "S3 region code, e.g. ap-southeast-1.",
"storage.providers.fields.s3.region.label": "Region",
"storage.providers.fields.s3.region.placeholder": "ap-southeast-1",
"storage.providers.fields.s3.secret-key.label": "Secret Access Key",
"storage.providers.fields.s3.secret-key.placeholder": "************",
"storage.providers.modal.create.description": "Configure a new storage backend for your photos.",
"storage.providers.modal.create.title": "Add storage provider",
"storage.providers.modal.edit.description": "Update provider credentials and options.",
"storage.providers.modal.edit.title": "Edit provider",
"storage.providers.modal.fields.name.label": "Display name",
"storage.providers.modal.fields.name.placeholder": "e.g., Production S3",
"storage.providers.modal.fields.type.label": "Provider type",
"storage.providers.modal.fields.type.placeholder": "Select a provider",
"storage.providers.modal.sections.basic": "Basic information",
"storage.providers.modal.sections.connection": "Connection configuration",
"storage.providers.prompt.sync.cancel": "Maybe later",
"storage.providers.prompt.sync.confirm": "Start syncing",
"storage.providers.prompt.sync.description": "Storage provider configuration is saved. Go to Data Sync now to scan storage and update the database?",
"storage.providers.prompt.sync.title": "Sync photos now?",
"storage.providers.security.description": "Sensitive credentials (keys, tokens, etc.) are encrypted with {{algorithm}} to protect your data.",
"storage.providers.security.helper": "{{algorithm}} provides authenticated encryption to keep data confidential and tamper-proof.",
"storage.providers.security.title": "Storage security",
"storage.providers.status.dirty": "{{total}} provider(s) pending save",
"storage.providers.status.error": "Save failed: {{reason}}",
"storage.providers.status.saved": "✓ Storage configuration saved",
"storage.providers.status.summary": "{{total}} storage provider(s) • {{active}} active",
"storage.providers.types.eagle": "Eagle library",
"storage.providers.types.github": "GitHub repository",
"storage.providers.types.local": "Local storage",
"storage.providers.types.minio": "MinIO",
"storage.providers.types.s3": "AWS S3 / compatible object storage",
"superadmin.brand": "Afilmory · System Settings",
"superadmin.builder-debug.actions.cancel": "Cancel Debug",
"superadmin.builder-debug.actions.start": "Run Debug",
@@ -317,6 +697,7 @@
"superadmin.settings.message.unknown-error": "Unknown error",
"superadmin.settings.stats.remaining": "Remaining registration slots",
"superadmin.settings.stats.total-users": "Total users",
"superadmin.settings.stats.unlimited": "Unlimited",
"superadmin.settings.title": "System Settings",
"superadmin.tenants.button.ban": "Ban",
"superadmin.tenants.button.processing": "Working…",
@@ -341,5 +722,17 @@
"superadmin.tenants.toast.ban-success": "Tenant {{name}} has been banned.",
"superadmin.tenants.toast.plan-error": "Failed to update subscription plan.",
"superadmin.tenants.toast.plan-success": "{{name}} switched to the {{planId}} plan.",
"superadmin.tenants.toast.unban-success": "Tenant {{name}} is no longer banned."
"superadmin.tenants.toast.unban-success": "Tenant {{name}} is no longer banned.",
"welcome.tenant-missing.code": "404",
"welcome.tenant-missing.description": "We could not find a space at this address. It may have been removed or the link is incorrect. Double-check the URL, or register your own space to continue using Afilmory.",
"welcome.tenant-missing.home": "Back to home",
"welcome.tenant-missing.register": "Register now",
"welcome.tenant-missing.request": "Requested host:",
"welcome.tenant-missing.title": "Space Not Found",
"welcome.tenant-restricted.code": "403",
"welcome.tenant-restricted.description": "This hostname is reserved by the system and cannot serve a dashboard or public site. To keep exploring Afilmory, use another hostname or register your own dedicated space.",
"welcome.tenant-restricted.home": "Back to home",
"welcome.tenant-restricted.register": "Create a new space",
"welcome.tenant-restricted.request": "Requested host:",
"welcome.tenant-restricted.title": "Space Reserved"
}

View File

@@ -70,7 +70,7 @@
"common.unknown-error": "未知错误",
"dashboard.overview.activity.empty": "暂无最近活动,上传照片后即可看到这里的动态。",
"dashboard.overview.activity.error": "无法获取活动数据,请稍后再试。",
"dashboard.overview.activity.id-label": "ID:",
"dashboard.overview.activity.id-label": "ID",
"dashboard.overview.activity.no-preview": "暂无预览",
"dashboard.overview.activity.size-unknown": "大小未知",
"dashboard.overview.activity.subtitle": "展示最近 {{count}} 次上传和同步记录",
@@ -78,7 +78,7 @@
"dashboard.overview.activity.taken-at": "拍摄时间 {{time}}",
"dashboard.overview.activity.uploaded-at": "上传于 {{time}}",
"dashboard.overview.page.description": "掌握图库运行状态与最近同步活动",
"dashboard.overview.page.title": "Dashboard",
"dashboard.overview.page.title": "仪表盘",
"dashboard.overview.section.activity.title": "最近活动",
"dashboard.overview.stats.month.helper.equal": "与上月持平",
"dashboard.overview.stats.month.helper.first": "首次出现上传记录",
@@ -182,18 +182,268 @@
"photos.conflict.metadata.label": "元数据不一致",
"photos.conflict.missing.description": "数据库存在记录,但对应的存储对象已无法访问。",
"photos.conflict.missing.label": "存储缺失",
"photos.library.actions.all-selected": "已全选",
"photos.library.actions.clear-selection": "清除选择",
"photos.library.actions.delete": "删除",
"photos.library.actions.edit-tags": "编辑标签",
"photos.library.actions.select-all": "全选",
"photos.library.actions.selected-count": "已选 {{count}} 项",
"photos.library.actions.upload": "上传文件",
"photos.library.actions.upload-short": "上传",
"photos.library.card.delete": "删除素材",
"photos.library.card.device-unknown": "未知设备",
"photos.library.card.edit-tags": "编辑标签",
"photos.library.card.no-preview": "暂无预览",
"photos.library.card.select": "选择",
"photos.library.card.selected": "已选择",
"photos.library.card.size-unknown": "大小未知",
"photos.library.card.view-exif": "查看 EXIF",
"photos.library.delete.cancel": "取消",
"photos.library.delete.confirm": "删除",
"photos.library.delete.description": "此操作无法撤销。要继续删除「{{name}}」吗?勾选下方选项可同时删除远程存储文件。",
"photos.library.delete.option.description": "勾选后会删除远程原件与缩略图。",
"photos.library.delete.option.title": "同时删除存储文件",
"photos.library.delete.title": "确定要删除该素材?",
"photos.library.empty.description": "使用上传按钮将照片添加到图库。",
"photos.library.empty.title": "还没有照片",
"photos.library.exif.empty": "该素材没有可用的 EXIF 数据。",
"photos.library.exif.file": "文件:{{value}}",
"photos.library.exif.rows.altitude": "海拔",
"photos.library.exif.rows.altitude-below": "{{value}} 米(低于海平面)",
"photos.library.exif.rows.altitude-value": "{{value}} 米",
"photos.library.exif.rows.aperture": "光圈",
"photos.library.exif.rows.aspect-ratio": "纵横比",
"photos.library.exif.rows.author": "作者",
"photos.library.exif.rows.brightness": "亮度值",
"photos.library.exif.rows.captured-at": "拍摄时间",
"photos.library.exif.rows.color-space": "色彩空间",
"photos.library.exif.rows.copyright": "版权",
"photos.library.exif.rows.device": "相机",
"photos.library.exif.rows.eq-focal-length": "等效 35mm 焦距",
"photos.library.exif.rows.exposure-compensation": "曝光补偿",
"photos.library.exif.rows.exposure-mode": "曝光模式",
"photos.library.exif.rows.exposure-program": "曝光程序",
"photos.library.exif.rows.file-format": "格式",
"photos.library.exif.rows.file-size": "文件大小",
"photos.library.exif.rows.flash": "闪光灯",
"photos.library.exif.rows.focal-length": "焦距",
"photos.library.exif.rows.iso": "ISO 感光度",
"photos.library.exif.rows.latitude": "纬度",
"photos.library.exif.rows.lens": "镜头",
"photos.library.exif.rows.light-source": "光源",
"photos.library.exif.rows.longitude": "经度",
"photos.library.exif.rows.megapixels": "像素数",
"photos.library.exif.rows.metering-mode": "测光模式",
"photos.library.exif.rows.photo-id": "照片 ID",
"photos.library.exif.rows.rating": "评级",
"photos.library.exif.rows.resolution": "分辨率",
"photos.library.exif.rows.scale-factor": "35mm 换算系数",
"photos.library.exif.rows.scene-type": "场景类型",
"photos.library.exif.rows.sensor": "传感器",
"photos.library.exif.rows.shutter": "快门速度",
"photos.library.exif.rows.software": "软件",
"photos.library.exif.rows.time-offset": "时差",
"photos.library.exif.rows.timezone": "时区",
"photos.library.exif.rows.timezone-source": "时区来源",
"photos.library.exif.rows.title": "标题",
"photos.library.exif.rows.white-balance": "白平衡",
"photos.library.exif.sections.basic": "基础信息",
"photos.library.exif.sections.capture": "拍摄参数",
"photos.library.exif.sections.fuji": "富士配方",
"photos.library.exif.sections.location": "位置信息",
"photos.library.exif.sections.metadata": "元数据",
"photos.library.sort.by-captured": "按拍摄时间排序",
"photos.library.sort.by-uploaded": "按上传时间排序",
"photos.library.sort.order-asc": "最早在前",
"photos.library.sort.order-desc": "最新在前",
"photos.library.tags.modal.asset-count": "{{count}} 个素材",
"photos.library.tags.modal.cancel": "取消",
"photos.library.tags.modal.description.multiple": "所有选中素材将使用相同标签。标签也会影响远程存储路径。",
"photos.library.tags.modal.description.single": "标签决定远程存储路径,调整标签会同时移动原始文件与 Live Photo 视频。",
"photos.library.tags.modal.input": "输入标签并按 Enter或从推荐标签中选择",
"photos.library.tags.modal.no-selection": "未选中任何素材",
"photos.library.tags.modal.path.hint": "根据标签顺序生成",
"photos.library.tags.modal.path.preview": "新的存储路径预览",
"photos.library.tags.modal.path.sample": "示例路径(首个条目)",
"photos.library.tags.modal.save": "保存",
"photos.library.tags.modal.saving": "保存中…",
"photos.library.tags.modal.title": "为「{{name}}」编辑标签",
"photos.library.tags.toast.error": "无法更新标签",
"photos.library.tags.toast.error-description": "请稍后再试。",
"photos.library.tags.toast.multi-success": "已更新 {{count}} 个素材的标签",
"photos.library.tags.toast.single-success": "标签已更新",
"photos.library.tags.toast.success-description": "已根据新标签更新存储路径。",
"photos.page.description": "在此同步和管理服务器中的照片资产。",
"photos.page.title": "照片库",
"photos.sync.actions.button.apply": "同步照片",
"photos.sync.actions.button.preview": "预览同步",
"photos.sync.actions.toast.apply-success": "照片同步完成",
"photos.sync.actions.toast.error-description": "照片同步失败,请稍后再试。",
"photos.sync.actions.toast.error-title": "同步失败",
"photos.sync.actions.toast.preview-success": "预览同步完成",
"photos.sync.actions.toast.success-description": "新增 {{inserted}} · 更新 {{updated}} · 冲突 {{conflicts}} · 错误 {{errors}}",
"photos.sync.conflicts.actions.all-database": "全部采用数据库版本",
"photos.sync.conflicts.actions.all-storage": "全部采用存储版本",
"photos.sync.conflicts.actions.clear-selection": "清除选择",
"photos.sync.conflicts.actions.hide-details": "收起详情",
"photos.sync.conflicts.actions.open-storage": "打开",
"photos.sync.conflicts.actions.prefer-database": "保留数据库",
"photos.sync.conflicts.actions.prefer-storage": "保留存储",
"photos.sync.conflicts.actions.selected-database": "将数据库版本应用到所选项",
"photos.sync.conflicts.actions.selected-storage": "将存储版本应用到所选项",
"photos.sync.conflicts.actions.view-details": "查看详情",
"photos.sync.conflicts.actions.view-original": "查看原图",
"photos.sync.conflicts.description": "这些冲突需要人工确认,可多选条目加速处理。",
"photos.sync.conflicts.info.conflict-key": "冲突 Key",
"photos.sync.conflicts.info.first-detected": "发现时间:{{time}}",
"photos.sync.conflicts.info.last-updated": "最近更新:{{time}}",
"photos.sync.conflicts.info.photo-id-fallback": "缺少 Photo ID",
"photos.sync.conflicts.info.storage-key": "存储 Key",
"photos.sync.conflicts.preview.common.dimensions": "尺寸:",
"photos.sync.conflicts.preview.common.id": "ID",
"photos.sync.conflicts.preview.common.size": "大小:",
"photos.sync.conflicts.preview.common.updated-at": "更新时间:",
"photos.sync.conflicts.preview.database.empty": "无可用数据库记录",
"photos.sync.conflicts.preview.database.title": "数据库记录",
"photos.sync.conflicts.preview.storage.key": "Key",
"photos.sync.conflicts.preview.storage.title": "存储对象",
"photos.sync.conflicts.prompts.bulk": "是否处理 {{scope}} 并 {{strategy}}",
"photos.sync.conflicts.prompts.cancel": "取消",
"photos.sync.conflicts.prompts.confirm": "确认",
"photos.sync.conflicts.prompts.scope-all": "全部待处理冲突",
"photos.sync.conflicts.prompts.scope-selected": "所选 {{count}} 个冲突",
"photos.sync.conflicts.prompts.single": "是否处理冲突 {{identifier}} 并 {{strategy}}",
"photos.sync.conflicts.prompts.title": "确认操作",
"photos.sync.conflicts.selection.clear": "清除选择",
"photos.sync.conflicts.selection.none": "未选择任何项",
"photos.sync.conflicts.selection.selected": "已选 {{count}} 项",
"photos.sync.conflicts.strategy.database": "保留数据库版本",
"photos.sync.conflicts.strategy.storage": "保留存储版本",
"photos.sync.conflicts.title": "待处理冲突",
"photos.sync.conflicts.toast.no-original": "此记录没有原始资源预览。",
"photos.sync.conflicts.toast.none": "当前没有需要处理的冲突。",
"photos.sync.conflicts.toast.open-storage-failed": "无法打开存储对象",
"photos.sync.conflicts.toast.select-required": "请先选择需要处理的冲突。",
"photos.sync.conflicts.total": "总计:{{count}}",
"photos.sync.metadata.database": "元数据 · 数据库",
"photos.sync.metadata.etag": "ETag实体标签",
"photos.sync.metadata.hash": "元数据哈希",
"photos.sync.metadata.none": "无",
"photos.sync.metadata.size": "大小",
"photos.sync.metadata.storage": "元数据 · 存储",
"photos.sync.metadata.unknown": "未知",
"photos.sync.metadata.updated-at": "更新时间",
"photos.sync.progress.heading.error": "同步失败",
"photos.sync.progress.heading.preview": "正在运行预览同步",
"photos.sync.progress.heading.running": "正在运行同步",
"photos.sync.progress.logs.detail.error": "错误 {{value}}",
"photos.sync.progress.logs.detail.live-photo": "包含实况照片",
"photos.sync.progress.logs.detail.live-photo-absent": "无实况照片",
"photos.sync.progress.logs.detail.manifest": "包含上一份清单",
"photos.sync.progress.logs.detail.manifest-absent": "无上一份清单",
"photos.sync.progress.logs.detail.result": "结果 {{value}}",
"photos.sync.progress.logs.level.error": "错误",
"photos.sync.progress.logs.level.info": "信息",
"photos.sync.progress.logs.level.success": "成功",
"photos.sync.progress.logs.level.warn": "警告",
"photos.sync.progress.logs.recent": "最新 {{count}} 条",
"photos.sync.progress.logs.title": "同步日志",
"photos.sync.progress.recent.no-further": "无需进一步操作",
"photos.sync.progress.recent.progress": "进度:{{processed}} / {{total}}",
"photos.sync.progress.recent.title": "最近操作",
"photos.sync.progress.stage-status.completed": "已完成",
"photos.sync.progress.stage-status.pending": "待开始",
"photos.sync.progress.stage-status.running": "进行中",
"photos.sync.progress.stages.conflicts.description": "检测存储元数据与数据库记录的差异。",
"photos.sync.progress.stages.conflicts.label": "校验元数据",
"photos.sync.progress.stages.missing.description": "将存储中的新对象同步到数据库。",
"photos.sync.progress.stages.missing.label": "导入新照片",
"photos.sync.progress.stages.orphan.description": "标记数据库中对应存储对象缺失的记录。",
"photos.sync.progress.stages.orphan.label": "识别孤立记录",
"photos.sync.progress.stages.progress": "{{processed}} / {{total}} 项",
"photos.sync.progress.stages.reconciliation.description": "更新记录状态以匹配最新元数据。",
"photos.sync.progress.stages.reconciliation.label": "状态对账",
"photos.sync.progress.status.error": "已停止",
"photos.sync.progress.status.running": "进行中",
"photos.sync.progress.subtitle.error": "同步过程中发生错误,请查看日志后重试。",
"photos.sync.progress.subtitle.preview": "正在模拟同步操作,预览模式不会写入数据库。",
"photos.sync.progress.subtitle.running": "正在对比存储与数据库,请保持此页面开启以查看实时进度。",
"photos.sync.result.actions.applied": "已应用",
"photos.sync.result.actions.collapse": "收起详情",
"photos.sync.result.actions.expand": "查看详情",
"photos.sync.result.actions.pending": "未应用",
"photos.sync.result.alerts.open-original-failed": "无法打开原始照片",
"photos.sync.result.duration.less-than-second": "少于 1 秒",
"photos.sync.result.duration.minutes": "{{count}} 分钟",
"photos.sync.result.duration.seconds": "{{count}} 秒",
"photos.sync.result.filters.all": "全部",
"photos.sync.result.history.completed-at": "完成时间 {{time}}",
"photos.sync.result.history.duration": "耗时 {{duration}}",
"photos.sync.result.history.heading": "最近完成的同步",
"photos.sync.result.history.mode.live": "实时模式 · 已应用更改",
"photos.sync.result.history.mode.preview": "预览模式 · 未写入数据库",
"photos.sync.result.history.operations": "操作:{{count}}",
"photos.sync.result.info.conflict-type": "冲突类型:",
"photos.sync.result.info.photo-id": "Photo ID",
"photos.sync.result.info.storage-key": "存储 Key",
"photos.sync.result.manifest.empty": "暂无清单数据",
"photos.sync.result.operations.count": "操作:{{count}}",
"photos.sync.result.operations.filter-label": "· 筛选:",
"photos.sync.result.status.empty.description": "请在设置中配置并启用存储提供方,然后使用同步控制开始处理。预览模式安全且不会修改数据。",
"photos.sync.result.status.empty.title": "尚无同步记录",
"photos.sync.result.status.loading.description": "正在获取最新同步任务,请稍候…",
"photos.sync.result.status.loading.title": "正在加载同步状态",
"photos.sync.result.summary.description.latest": "下方展示最近一次同步结果。",
"photos.sync.result.summary.description.live": "最近一次同步以实时模式执行,并写入了数据库。",
"photos.sync.result.summary.description.preview": "最近运行的是预览模式,未对数据库写入任何更改。",
"photos.sync.result.summary.heading": "同步概览",
"photos.sync.result.summary.labels.completed": "已同步",
"photos.sync.result.summary.labels.conflicts": "冲突",
"photos.sync.result.summary.labels.database-records": "数据库记录",
"photos.sync.result.summary.labels.deleted": "已删除",
"photos.sync.result.summary.labels.errors": "错误",
"photos.sync.result.summary.labels.inserted": "新增照片",
"photos.sync.result.summary.labels.pending": "待处理",
"photos.sync.result.summary.labels.skipped": "已跳过",
"photos.sync.result.summary.labels.storage-objects": "存储对象",
"photos.sync.result.summary.labels.updated": "已更新",
"photos.sync.result.table.empty.filtered": "没有符合当前筛选条件的操作。",
"photos.sync.result.table.empty.none": "同步完成,但没有可供查看的操作。",
"photos.sync.result.table.mode.live": "实时模式 · 结果已保存",
"photos.sync.result.table.mode.preview": "预览模式 · 未应用更改",
"photos.sync.result.table.title": "同步操作详情",
"photos.sync.toasts.conflict-batch-error": "部分冲突处理失败",
"photos.sync.toasts.conflict-batch-success": "{{strategy}} 已处理 {{count}} 个冲突。",
"photos.sync.toasts.conflict-database": "已保留数据库记录,忽略存储差异。",
"photos.sync.toasts.conflict-error": "冲突处理失败",
"photos.sync.toasts.conflict-error-desc": "当前无法解决冲突,请稍后再试。",
"photos.sync.toasts.conflict-resolved": "冲突已解决",
"photos.sync.toasts.conflict-select": "请先选择至少一个冲突项。",
"photos.sync.toasts.conflict-storage": "已将存储版本写入数据库。",
"photos.tabs.library": "图库管理",
"photos.tabs.storage": "素材存储",
"photos.tabs.sync": "存储同步",
"photos.tabs.usage": "用量记录",
"photos.usage.photo-created.description": "通过上传或同步新增的照片资产。",
"photos.usage.events.description": "与计费相关的最新操作,如上传、删除与同步。",
"photos.usage.events.empty.description": "上传照片或运行同步后会在此显示计费事件。",
"photos.usage.events.empty.title": "暂无使用数据",
"photos.usage.events.metadata.empty": "— / 无",
"photos.usage.events.metadata.more": "+{{count}} 项",
"photos.usage.events.metadata.value-unknown": "未知",
"photos.usage.events.title": "最近的使用事件",
"photos.usage.events.total": "{{count}} 条事件",
"photos.usage.events.unit.byte": "字节",
"photos.usage.events.unit.count": "次数",
"photos.usage.events.unit.label": "单位:{{unit}}",
"photos.usage.photo-created.description": "通过上传或同步新增的照片。",
"photos.usage.photo-created.label": "新增照片",
"photos.usage.photo-deleted.description": "从图库或存储中除的照片资产。",
"photos.usage.photo-deleted.description": "从图库或存储中除的照片。",
"photos.usage.photo-deleted.label": "删除照片",
"photos.usage.sync-completed.description": "一次数据同步执行完成时记录的汇总事件。",
"photos.usage.sync-completed.label": "同步运行",
"photos.usage.summary.description": "按事件类型汇总的累计统计。",
"photos.usage.summary.refresh": "刷新",
"photos.usage.summary.title": "使用概览",
"photos.usage.sync-completed.description": "数据同步完成时产生的汇总事件。",
"photos.usage.sync-completed.label": "同步完成",
"plan.badge.current": "当前方案",
"plan.badge.internal": "内部方案",
"plan.checkout.coming-soon": "敬请期待",
@@ -209,7 +459,7 @@
"plan.quotas.label.maxSyncObjectSizeMb": "同步素材大小",
"plan.quotas.label.maxUploadSizeMb": "单次上传大小",
"plan.quotas.label.monthlyAssetProcessLimit": "每月可新增照片",
"plan.quotas.unit.megabytes": "{{value}} MB",
"plan.quotas.unit.megabytes": "{{value}} MB(兆字节)",
"plan.quotas.unit.photos": "{{value}} 张",
"plan.quotas.unlimited": "无限制",
"plan.toast.checkout-failure": "无法创建订阅结算会话,请稍后再试。",
@@ -234,68 +484,198 @@
"settings.site.title": "站点设置",
"settings.user.description": "维护展示在前台的作者资料、头像与别名。",
"settings.user.title": "用户信息",
"site.settings.banner.dirty": "{{count}} 项设置待保存",
"site.settings.banner.fail": "保存失败:",
"site.settings.banner.success": "设置已成功同步",
"site.settings.banner.synced": "所有设置均为最新状态",
"site.settings.button.save": "保存更改",
"site.settings.button.saving": "保存中…",
"site.settings.error.load-prefix": "无法加载站点设置:",
"site.settings.error.unknown": "未知错误",
"site.user.blocker.cancel": "留在此页",
"site.user.blocker.confirm": "仍然离开",
"site.user.blocker.description": "您有未保存的更改,离开将会丢失,确定继续?",
"site.user.blocker.title": "离开前请先保存",
"site.user.button.save": "保存更改",
"site.user.button.saving": "保存中…",
"site.user.error.loading": "无法加载用户资料",
"site.user.form.avatar.helper": "支持 http(s) 或 // 开头的链接,留空则回退为首字母头像。",
"site.user.form.avatar.label": "头像 URL",
"site.user.form.avatar.placeholder": "https://cdn.example.com/avatar.png示例",
"site.user.form.display.helper": "显示在头像旁。留空则使用作者姓名。",
"site.user.form.display.label": "展示名称",
"site.user.form.display.placeholder": "可选,例如 innei.photo",
"site.user.form.name.helper": "会公开展示,并写入 RSS 的作者/编辑字段。",
"site.user.form.name.label": "作者姓名",
"site.user.form.name.placeholder": "例如 Innei",
"site.user.form.username.helper": "内部用于识别作者,不会公开显示。",
"site.user.form.username.label": "用户名(可选)",
"site.user.form.username.placeholder": "例如 innei",
"site.user.header.badge": "作者资料",
"site.user.header.description": "用于站点头部、RSS 与社交卡片,请保持与个人品牌一致。",
"site.user.header.title": "公开作者身份",
"site.user.preview.avatar-alt": "作者头像预览",
"site.user.preview.fallback": "作者",
"site.user.preview.last-updated": "最近更新:{{time}}",
"site.user.preview.never-updated": "尚未更新",
"site.user.toast.error": "保存资料失败",
"site.user.toast.error-description": "请检查输入后重试。",
"site.user.toast.success": "资料已更新",
"storage.providers.actions.add": "添加存储源",
"storage.providers.actions.cancel": "取消",
"storage.providers.actions.create": "创建存储源",
"storage.providers.actions.save": "保存更改",
"storage.providers.actions.saving": "保存中…",
"storage.providers.blocker.cancel": "留在此页",
"storage.providers.blocker.confirm": "仍然离开",
"storage.providers.blocker.description": "当前存储配置尚未保存,离开页面会丢失这些修改,确定继续?",
"storage.providers.blocker.title": "请先保存存储配置",
"storage.providers.card.active": "已启用",
"storage.providers.card.edit": "编辑",
"storage.providers.card.make-active": "设为启用",
"storage.providers.card.make-inactive": "设为停用",
"storage.providers.card.preview.fallback": "存储提供方",
"storage.providers.card.preview.not-configured": "未配置",
"storage.providers.card.untitled": "未命名存储",
"storage.providers.empty.action": "添加存储源",
"storage.providers.empty.description": "添加一个存储提供方,系统才能同步并管理远程照片。",
"storage.providers.empty.title": "尚未配置存储提供方",
"storage.providers.error.load": "无法加载存储设置",
"storage.providers.fields.github.branch.description": "可选,要同步的分支。",
"storage.providers.fields.github.branch.helper": "默认 master/main如有不同请填写完整分支名。",
"storage.providers.fields.github.branch.label": "分支",
"storage.providers.fields.github.branch.placeholder": "main示例",
"storage.providers.fields.github.owner.description": "GitHub 用户或组织名称。",
"storage.providers.fields.github.owner.label": "仓库拥有者",
"storage.providers.fields.github.owner.placeholder": "afilmory示例",
"storage.providers.fields.github.path.description": "可选,限制同步的仓库路径。",
"storage.providers.fields.github.path.label": "仓库路径",
"storage.providers.fields.github.path.placeholder": "public/photos可选",
"storage.providers.fields.github.repo.description": "存放照片的仓库。",
"storage.providers.fields.github.repo.label": "仓库名称",
"storage.providers.fields.github.repo.placeholder": "photo-assets示例",
"storage.providers.fields.github.token.description": "用于私有仓库的 Personal Access Token。",
"storage.providers.fields.github.token.label": "访问令牌",
"storage.providers.fields.github.token.placeholder": "ghp_xxxxxxxxxxxxxxxxxxxx示例",
"storage.providers.fields.github.use-raw.description": "生成公开链接时使用 raw.githubusercontent.com。",
"storage.providers.fields.github.use-raw.helper": "如果通过自定义域名分发文件,请设为 false。",
"storage.providers.fields.github.use-raw.label": "使用 raw 链接",
"storage.providers.fields.github.use-raw.placeholder": "true / false布尔",
"storage.providers.fields.s3.access-key.label": "Access Key ID公钥",
"storage.providers.fields.s3.access-key.placeholder": "AKIAxxxxxxxxxxxx示例",
"storage.providers.fields.s3.bucket.description": "存放照片的 S3 Bucket 名称。",
"storage.providers.fields.s3.bucket.label": "Bucket 名称",
"storage.providers.fields.s3.bucket.placeholder": "afilmory-photos示例",
"storage.providers.fields.s3.custom-domain.description": "用于生成公开照片链接的域名。",
"storage.providers.fields.s3.custom-domain.label": "自定义公开域名",
"storage.providers.fields.s3.custom-domain.placeholder": "https://cdn.example.com示例",
"storage.providers.fields.s3.endpoint.description": "S3 兼容服务的自定义 Endpoint。",
"storage.providers.fields.s3.endpoint.helper": "使用 AWS S3 时可留空MinIO 或其他厂商需要填写。",
"storage.providers.fields.s3.endpoint.label": "自定义 Endpoint",
"storage.providers.fields.s3.endpoint.placeholder": "https://s3.example.com示例",
"storage.providers.fields.s3.exclude-regex.description": "可选,跳过匹配该正则表达式的文件。",
"storage.providers.fields.s3.exclude-regex.helper": "使用 JavaScript 兼容的正则表达式。",
"storage.providers.fields.s3.exclude-regex.label": "排除规则(正则)",
"storage.providers.fields.s3.exclude-regex.placeholder": "\\.(tmp|bak)$",
"storage.providers.fields.s3.max-files.description": "可选,每次扫描的最大文件数。",
"storage.providers.fields.s3.max-files.label": "最大文件数",
"storage.providers.fields.s3.max-files.placeholder": "1000可选",
"storage.providers.fields.s3.prefix.description": "可选,仅扫描该前缀下的对象。",
"storage.providers.fields.s3.prefix.label": "路径前缀",
"storage.providers.fields.s3.prefix.placeholder": "photos/(前缀)",
"storage.providers.fields.s3.region.description": "S3 区域代码,例如 ap-southeast-1。",
"storage.providers.fields.s3.region.label": "区域",
"storage.providers.fields.s3.region.placeholder": "ap-southeast-1示例",
"storage.providers.fields.s3.secret-key.label": "Secret Access Key私钥",
"storage.providers.fields.s3.secret-key.placeholder": "************(示例)",
"storage.providers.modal.create.description": "为照片配置新的存储后端。",
"storage.providers.modal.create.title": "添加存储提供方",
"storage.providers.modal.edit.description": "更新该提供方的凭证和选项。",
"storage.providers.modal.edit.title": "编辑存储提供方",
"storage.providers.modal.fields.name.label": "显示名称",
"storage.providers.modal.fields.name.placeholder": "例如 Production S3",
"storage.providers.modal.fields.type.label": "提供类型",
"storage.providers.modal.fields.type.placeholder": "请选择提供方",
"storage.providers.modal.sections.basic": "基础信息",
"storage.providers.modal.sections.connection": "连接配置",
"storage.providers.prompt.sync.cancel": "稍后再说",
"storage.providers.prompt.sync.confirm": "开始同步",
"storage.providers.prompt.sync.description": "存储提供方配置已保存。是否立即前往数据同步,扫描存储并更新数据库?",
"storage.providers.prompt.sync.title": "立即同步照片?",
"storage.providers.security.description": "密钥、令牌等敏感凭据会使用 {{algorithm}} 加密以保护数据。",
"storage.providers.security.helper": "{{algorithm}} 提供认证加密,确保数据保密且不可篡改。",
"storage.providers.security.title": "存储安全",
"storage.providers.status.dirty": "{{total}} 个存储源待保存",
"storage.providers.status.error": "保存失败:{{reason}}",
"storage.providers.status.saved": "✓ 存储配置已保存",
"storage.providers.status.summary": "{{total}} 个存储提供方 • {{active}} 个启用",
"storage.providers.types.eagle": "Eagle 素材库",
"storage.providers.types.github": "GitHub 仓库",
"storage.providers.types.local": "本地存储",
"storage.providers.types.minio": "MinIO 存储",
"storage.providers.types.s3": "AWS S3 / 兼容对象存储",
"superadmin.brand": "Afilmory · 系统管理",
"superadmin.builder-debug.actions.cancel": "取消调试",
"superadmin.builder-debug.actions.start": "启动调试",
"superadmin.builder-debug.api.missing-result": "调试过程中未收到最终结果,连接已终止。",
"superadmin.builder-debug.actions.start": "开始调试",
"superadmin.builder-debug.api.missing-result": "在连接结束前未收到最终调试结果。",
"superadmin.builder-debug.api.request-failed": "调试请求失败:{{status}} {{statusText}}",
"superadmin.builder-debug.description": "该工具用于单张图片的 Builder 管线验收。调试过程中不会写入数据库,所有上传与生成的文件会在任务完成后立刻清理。",
"superadmin.builder-debug.description": "在不触碰持久化数据的情况下验证单张图片的构建管线。",
"superadmin.builder-debug.input.clear": "清除",
"superadmin.builder-debug.input.file-meta": "{{size}} · {{type}}",
"superadmin.builder-debug.input.max": "仅支持张图片,大 25 MB",
"superadmin.builder-debug.input.placeholder": "点击或拖图片到此区域",
"superadmin.builder-debug.input.subtitle": "选择一张原始图片,系统将模拟 Builder 处理链路。",
"superadmin.builder-debug.input.file-meta": "{{size}} · {{type}}(源文件)",
"superadmin.builder-debug.input.max": "仅支持 1 张图片,大小不超过 25 MB",
"superadmin.builder-debug.input.placeholder": "点击或拖图片",
"superadmin.builder-debug.input.subtitle": "选择一张源图片来模拟 Builder 管线。",
"superadmin.builder-debug.input.title": "调试输入",
"superadmin.builder-debug.log.level.error": "ERROR",
"superadmin.builder-debug.log.level.info": "INFO",
"superadmin.builder-debug.log.level.success": "SUCCESS",
"superadmin.builder-debug.log.level.warn": "WARN",
"superadmin.builder-debug.log.level.error": "错误",
"superadmin.builder-debug.log.level.info": "信息",
"superadmin.builder-debug.log.level.success": "成功",
"superadmin.builder-debug.log.level.warn": "警告",
"superadmin.builder-debug.log.message.complete": "构建完成 · 结果 {{resultType}}",
"superadmin.builder-debug.log.message.start": "上传 {{filename}}准备行 Builder",
"superadmin.builder-debug.log.status.complete": "COMPLETE",
"superadmin.builder-debug.log.status.error": "ERROR",
"superadmin.builder-debug.log.status.start": "START",
"superadmin.builder-debug.logs.empty": "无日志",
"superadmin.builder-debug.logs.initializing": "正在初始化调试环境...",
"superadmin.builder-debug.log.message.start": "正在上传 {{filename}} · 准备行 Builder",
"superadmin.builder-debug.log.status.complete": "完成",
"superadmin.builder-debug.log.status.error": "错误",
"superadmin.builder-debug.log.status.start": "开始",
"superadmin.builder-debug.logs.empty": "无日志",
"superadmin.builder-debug.logs.initializing": "正在准备调试环境",
"superadmin.builder-debug.logs.source": "来源Builder + Data Sync Relay",
"superadmin.builder-debug.logs.subtitle": "最新 {{count}} 条消息",
"superadmin.builder-debug.logs.title": "实时日志",
"superadmin.builder-debug.notes.keep-page-open": "行期间请保持页面开启。调试依赖与 Data Sync 相同的 builder 配置,并实时返回日志。",
"superadmin.builder-debug.output.after-run": "运行调试后,这里会显示 manifest 内容与概要。",
"superadmin.builder-debug.output.copy": "复制 manifest",
"superadmin.builder-debug.output.no-manifest": "当前任务未生成 manifest 数据。",
"superadmin.builder-debug.output.subtitle": "展示 Builder 返回的 manifest 摘要",
"superadmin.builder-debug.notes.keep-page-open": "任务运行期间请保持页面开启。日志来自与数据同步相同的 Builder 配置。",
"superadmin.builder-debug.output.after-run": "运行调试任务后会在此显示清单详情。",
"superadmin.builder-debug.output.copy": "复制清单",
"superadmin.builder-debug.output.no-manifest": "此次运行未产生清单数据。",
"superadmin.builder-debug.output.subtitle": "查看 Builder 返回的清单摘要",
"superadmin.builder-debug.output.title": "调试输出",
"superadmin.builder-debug.recent.file": "文件",
"superadmin.builder-debug.recent.size": "大小",
"superadmin.builder-debug.recent.storage-key": "Storage Key",
"superadmin.builder-debug.recent.title": "最近一次任务",
"superadmin.builder-debug.safety.items.no-db": "不写入照片资产数据库记录",
"superadmin.builder-debug.safety.items.no-storage": "不在存储中保留任何调试产物",
"superadmin.builder-debug.safety.items.realtime": "所有日志实时输出,供排查使用",
"superadmin.builder-debug.recent.storage-key": "存储 Key",
"superadmin.builder-debug.recent.title": "最近任务",
"superadmin.builder-debug.safety.items.no-db": "不会向数据库写入照片记录",
"superadmin.builder-debug.safety.items.no-storage": "不在存储中保留调试产物",
"superadmin.builder-debug.safety.items.realtime": "日志实时输出,方便排查",
"superadmin.builder-debug.safety.title": "⚠️ 调试以安全模式运行:",
"superadmin.builder-debug.status.error": "失败",
"superadmin.builder-debug.status.idle": "就绪",
"superadmin.builder-debug.status.running": "调试中",
"superadmin.builder-debug.status.running": "运行中",
"superadmin.builder-debug.status.success": "已完成",
"superadmin.builder-debug.summary.cleaned": "产物清理",
"superadmin.builder-debug.summary.cleaned": "产物清理",
"superadmin.builder-debug.summary.cleaned-no": "否",
"superadmin.builder-debug.summary.cleaned-yes": "是",
"superadmin.builder-debug.summary.result-type": "结果类型",
"superadmin.builder-debug.summary.storage-key": "Storage Key",
"superadmin.builder-debug.summary.storage-key": "存储 Key",
"superadmin.builder-debug.summary.thumbnail": "缩略图 URL",
"superadmin.builder-debug.summary.thumbnail-missing": "未生成",
"superadmin.builder-debug.title": "Builder 调试工具",
"superadmin.builder-debug.title": "Builder 调试控制台",
"superadmin.builder-debug.toast.cancelled": "调试已取消",
"superadmin.builder-debug.toast.copy-failure.description": "请手动复制内容",
"superadmin.builder-debug.toast.copy-failure.description": "请手动复制内容",
"superadmin.builder-debug.toast.copy-failure.title": "复制失败",
"superadmin.builder-debug.toast.copy-success": "已复制 manifest 数据",
"superadmin.builder-debug.toast.copy-success": "清单已复制到剪贴板",
"superadmin.builder-debug.toast.failure-fallback": "调试失败,请检查后重试。",
"superadmin.builder-debug.toast.failure.title": "调试失败",
"superadmin.builder-debug.toast.manual-cancelled-log": "手动取消调试任务",
"superadmin.builder-debug.toast.manual-cancelled-message": "调试已被手动取消。",
"superadmin.builder-debug.toast.pick-file": "请选择需要调试的图片文件",
"superadmin.builder-debug.toast.success.description": "Builder 管线行成功,产物已清理。",
"superadmin.builder-debug.toast.manual-cancelled-log": "用户已停止调试运行",
"superadmin.builder-debug.toast.manual-cancelled-message": "调试会话已被手动取消。",
"superadmin.builder-debug.toast.pick-file": "在运行调试前请选择一张图片。",
"superadmin.builder-debug.toast.success.description": "Builder 管线行成功已清理产物。",
"superadmin.builder-debug.toast.success.title": "调试完成",
"superadmin.builder.title": "构建器设置",
"superadmin.nav.builder": "构建器",
@@ -317,6 +697,7 @@
"superadmin.settings.message.unknown-error": "未知错误",
"superadmin.settings.stats.remaining": "剩余可注册名额",
"superadmin.settings.stats.total-users": "当前用户总数",
"superadmin.settings.stats.unlimited": "不限",
"superadmin.settings.title": "系统设置",
"superadmin.tenants.button.ban": "封禁",
"superadmin.tenants.button.processing": "处理中…",
@@ -341,5 +722,17 @@
"superadmin.tenants.toast.ban-success": "已封禁租户 {{name}}",
"superadmin.tenants.toast.plan-error": "更新订阅失败",
"superadmin.tenants.toast.plan-success": "已将 {{name}} 切换到 {{planId}} 计划",
"superadmin.tenants.toast.unban-success": "已解除封禁 {{name}}"
"superadmin.tenants.toast.unban-success": "已解除封禁 {{name}}",
"welcome.tenant-missing.code": "404",
"welcome.tenant-missing.description": "未能在该地址找到空间,可能已被删除或链接有误。请检查 URL或注册自己的空间继续使用 Afilmory。",
"welcome.tenant-missing.home": "返回首页",
"welcome.tenant-missing.register": "立即注册",
"welcome.tenant-missing.request": "请求的主机:",
"welcome.tenant-missing.title": "未找到空间",
"welcome.tenant-restricted.code": "403",
"welcome.tenant-restricted.description": "该主机名被系统保留,无法用于仪表盘或公开站点。如需继续体验 Afilmory请使用其他主机名或注册专属空间。",
"welcome.tenant-restricted.home": "返回首页",
"welcome.tenant-restricted.register": "创建新空间",
"welcome.tenant-restricted.request": "请求的主机:",
"welcome.tenant-restricted.title": "空间已被保留"
}