feat(studio): localize catalog studio UI and polish responsive layout
Route all Catalog Studio strings through the admin.studio namespace (workspace, filters, review, inspector, quality, sync, scale32, organize imports and page titles/subtitles) with English fallback, and add shared StudioPage chrome plus a loading skeleton. Tighten mobile padding on the header and sub-bars.
This commit is contained in:
1 parent
d7d6382b60
commit
56676effc3
28 files changed
+1293
-634
No files matched your search
@@ -2570,6 +2570,8 @@
|
||||
"credits": "Credits",
|
||||
"points": "Points",
|
||||
"pointsType": "Type",
|
||||
"pointsTypeDuckets": "Duckets",
|
||||
"pointsTypeDiamonds": "Diamonds",
|
||||
"newPriceHint": "Applies to new offers only; moved offers keep their current price.",
|
||||
"parent": "Place pages under",
|
||||
"root": "Catalog root",
|
||||
@@ -6801,6 +6803,416 @@
|
||||
},
|
||||
"missing": "Missing: {parts}",
|
||||
"unknown": "Not verified: {parts}"
|
||||
},
|
||||
"rail": {
|
||||
"title": "Categories",
|
||||
"all": "All categories",
|
||||
"noMatches": "No matching categories.",
|
||||
"refreshFailed": "Catalog could not refresh.",
|
||||
"retry": "Retry",
|
||||
"updating": "Updating catalog…"
|
||||
},
|
||||
"nav": {
|
||||
"ariaLabel": "Catalog Studio",
|
||||
"furniture": "Furniture",
|
||||
"badges": "Badges",
|
||||
"clothing": "Clothing",
|
||||
"effects": "Effects",
|
||||
"pets": "Pets",
|
||||
"uploadFiles": "Upload files",
|
||||
"importFromSource": "Import from a source",
|
||||
"syncRepository": "Sync & repository",
|
||||
"catalogAudit": "Catalog audit",
|
||||
"repairIcons": "Repair icons",
|
||||
"catalogMaintenance": "Catalog maintenance",
|
||||
"nitroCleanup": "Nitro cleanup",
|
||||
"catalogManager": "Catalog manager",
|
||||
"importSync": "Import & sync",
|
||||
"maintenance": "Maintenance"
|
||||
},
|
||||
"aria": {
|
||||
"search": "Search furniture",
|
||||
"searchPlaceholder": "Search furni, classnames, keywords… (/)",
|
||||
"clearSearch": "Clear search",
|
||||
"hideCatalogTree": "Hide catalog tree",
|
||||
"showCatalogTree": "Show catalog tree",
|
||||
"source": "Furniture source",
|
||||
"type": "Furniture type",
|
||||
"category": "Furniture category",
|
||||
"status": "Import status",
|
||||
"sort": "Sort furniture",
|
||||
"importSpeed": "Import speed",
|
||||
"translateImport": "Translate this import",
|
||||
"defaultTranslation": "Default translation",
|
||||
"selection": "Furniture selection",
|
||||
"results": "Furniture results",
|
||||
"gridView": "Grid view",
|
||||
"listView": "List view",
|
||||
"selectAll": "Select all",
|
||||
"languageLabel": "Language {lang}",
|
||||
"selectItem": "Select {name}",
|
||||
"deselectItem": "Deselect {name}",
|
||||
"viewItem": "View {name}",
|
||||
"importItem": "Import {name}",
|
||||
"editNitro": "Edit nitro {name}",
|
||||
"regenerateNitro": "Regenerate nitro {name}",
|
||||
"scrollToTop": "Scroll to top",
|
||||
"closeCategoryPanel": "Close category panel",
|
||||
"catalogCategories": "Catalog categories",
|
||||
"closeCategories": "Close categories",
|
||||
"findCategory": "Find a category",
|
||||
"expandCategory": "Expand {name}",
|
||||
"collapseCategory": "Collapse {name}",
|
||||
"closeDetails": "Close furniture details"
|
||||
},
|
||||
"workspace": {
|
||||
"title": "Studio",
|
||||
"furnidata": "furnidata",
|
||||
"catalog": "Catalog",
|
||||
"results": "{count} results",
|
||||
"statInDb": "{count} in DB",
|
||||
"statInCatalog": "{count} in catalog",
|
||||
"statMissingNitro": "{count} missing nitro",
|
||||
"statTotal": "{count} total",
|
||||
"statPresent": "{count} present",
|
||||
"statToImport": "{count} to import",
|
||||
"official": "(official)"
|
||||
},
|
||||
"types": {
|
||||
"floor": "Floor",
|
||||
"wall": "Wall"
|
||||
},
|
||||
"filters": {
|
||||
"allTypes": "All types",
|
||||
"floorItems": "Floor items",
|
||||
"wallItems": "Wall items",
|
||||
"allCategories": "All categories",
|
||||
"allStatuses": "All statuses",
|
||||
"allStatusesCount": "All statuses ({count} available to import)",
|
||||
"statuses": {
|
||||
"notImported": "Not imported",
|
||||
"imported": "Imported",
|
||||
"inCatalog": "In catalog",
|
||||
"missingNitro": "Missing nitro",
|
||||
"missingFurnidata": "Missing furnidata",
|
||||
"missingCatalog": "Missing catalog entry"
|
||||
},
|
||||
"sorts": {
|
||||
"default": "Default order",
|
||||
"name": "Name A–Z",
|
||||
"classname": "Classname A–Z",
|
||||
"category": "Category",
|
||||
"importedFirst": "Imported first",
|
||||
"notImportedFirst": "Not imported first"
|
||||
},
|
||||
"reset": "Reset filters",
|
||||
"noNitro": "{count} no nitro",
|
||||
"noFurnidata": "{count} no furnidata"
|
||||
},
|
||||
"importOptions": {
|
||||
"summary": "Import options",
|
||||
"translationOn": "Translation on",
|
||||
"translationOff": "Translation off",
|
||||
"speed": "Speed",
|
||||
"parallel": "{count} parallel",
|
||||
"gentle": "1 (gentle)",
|
||||
"recommended": "4 (recommended)",
|
||||
"turbo": "12 (turbo)",
|
||||
"translate": "Translate",
|
||||
"noLanguages": "No languages",
|
||||
"allLanguages": "All languages",
|
||||
"languagesCount": "{count} languages",
|
||||
"languages": "Languages",
|
||||
"all": "All",
|
||||
"none": "None",
|
||||
"translateAllTitle": "Translate the entire catalog into all languages",
|
||||
"translateAll": "Translate entire catalog",
|
||||
"defaultFuture": "Default for future imports"
|
||||
},
|
||||
"selection": {
|
||||
"selectVisible": "Select visible",
|
||||
"deselectVisible": "Deselect visible",
|
||||
"loadingAll": "Loading all…",
|
||||
"selectAllResults": "Select all {count} results",
|
||||
"selectedInView": "{count} selected in this view",
|
||||
"clear": "Clear",
|
||||
"preparingReview": "Preparing review…",
|
||||
"importAllMissing": "Import all missing ({count})",
|
||||
"importSelected": "Import selected ({count})",
|
||||
"rebuildNitro": "Rebuild Nitro ({count})",
|
||||
"addMissingFurnidata": "Add missing furnidata ({count})",
|
||||
"deleteSelected": "Delete ({count})",
|
||||
"hiddenWarning": "Some selected furniture is hidden by your filters. Actions apply to the visible selection."
|
||||
},
|
||||
"items": {
|
||||
"tryAgain": "Try again",
|
||||
"noFound": "No furni found for “{query}”",
|
||||
"noFurni": "No furni to show yet",
|
||||
"noFoundHint": "Try a different search or clear your filters.",
|
||||
"noFurniHint": "Search a furni library to get started.",
|
||||
"noNitro": "No nitro",
|
||||
"noFurnidata": "No furnidata",
|
||||
"noCatalog": "No catalog",
|
||||
"imported": "Imported",
|
||||
"new": "New",
|
||||
"colName": "Name",
|
||||
"colClassname": "Classname",
|
||||
"colType": "Type",
|
||||
"colCategory": "Category",
|
||||
"colStatus": "Status",
|
||||
"colActions": "Actions",
|
||||
"loadingMore": "Loading more…",
|
||||
"scrollForMore": "Scroll for more",
|
||||
"endOfResults": "End of results",
|
||||
"defaultLanguages": "Default languages",
|
||||
"off": "Off"
|
||||
},
|
||||
"deleteDialog": {
|
||||
"title": "Delete imported furni",
|
||||
"body": "This permanently removes {name} from items_base and the catalog, plus its asset files.",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete"
|
||||
},
|
||||
"drawer": {
|
||||
"title": "Furni detail",
|
||||
"revision": "rev {revision}",
|
||||
"autoImportReady": "Auto-import ready",
|
||||
"automaticPlacement": "Automatic placement",
|
||||
"category": "Category",
|
||||
"price": "Price",
|
||||
"priceCredits": "{count} credits",
|
||||
"priceCreditsPoints": "{credits} credits + {points} points",
|
||||
"importAdd": "Import & add to catalog",
|
||||
"bundleMissing": "This furni is in the DB but its bundle file is missing.",
|
||||
"regenerate": "Regenerate .nitro",
|
||||
"editNitro": "Edit nitro",
|
||||
"deleteImported": "Delete imported furni"
|
||||
},
|
||||
"review": {
|
||||
"titleReview": "Review furniture import",
|
||||
"titleConfirm": "Confirm furniture import",
|
||||
"steps": "1. Select → 2. Review → 3. Import",
|
||||
"source": "Source:",
|
||||
"destination": "Destination:",
|
||||
"destinationValue": "local catalog, furnidata, bundle and icons",
|
||||
"translation": "Translation:",
|
||||
"selection": "Selection:",
|
||||
"selectionCount": "{count} furniture items",
|
||||
"checkingSource": "Checking source files… {count} checked",
|
||||
"retrySource": "Retry source check",
|
||||
"unknownSources": "Some sources could not be verified. The import will still try to download their files.",
|
||||
"sourceNotFound": "Source files not found by preliminary check: {name}",
|
||||
"checkingLocal": "Checking local data and assets…",
|
||||
"retryInspection": "Retry inspection",
|
||||
"incomplete": "Inspection is incomplete. Close and retry before importing.",
|
||||
"summary": "{ready} ready to import · {existing} already in SQL (complete missing data) · {blocked} conflicts · {warnings} source file warnings",
|
||||
"localUnverified": "Some local files could not be verified. Review the item details before continuing.",
|
||||
"statusNew": "New",
|
||||
"statusChanged": "Differences",
|
||||
"statusConflict": "Conflict",
|
||||
"statusUnknown": "Not fully verified",
|
||||
"statusPresent": "Present",
|
||||
"validatingFile": "Validating file…",
|
||||
"confirmIntro": "Only the {count} eligible items below will be submitted. Existing furniture keeps its local IDs, SQL settings and catalog offers. Missing assets and catalog entries are completed; furnidata is refreshed. Conflicts are excluded.",
|
||||
"confirmEstimates": "Automatic categories and prices below are estimates. Source rules may override them; final results appear in the import summary.",
|
||||
"placement": "{category} · {credits} credits + {points} points",
|
||||
"previous": "Previous items",
|
||||
"page": "Page {page} / {total}",
|
||||
"next": "Next items",
|
||||
"backToReview": "Back to review",
|
||||
"cancel": "Cancel",
|
||||
"continue": "Continue to confirmation",
|
||||
"startImport": "Start import ({count})",
|
||||
"uploadFailed": "Upload failed"
|
||||
},
|
||||
"inspector": {
|
||||
"furnidataUnreadable": "Local furnidata could not be read. Its values are not verified.",
|
||||
"field": "Field",
|
||||
"localNow": "Local now",
|
||||
"source": "Source",
|
||||
"fieldName": "Name",
|
||||
"fieldDescription": "Description",
|
||||
"fieldRevision": "Revision",
|
||||
"fieldType": "Type",
|
||||
"changed": "Changed",
|
||||
"notSupplied": "Not supplied",
|
||||
"compareNote": "Source values are compared with local data. Import may remap IDs and translate names. Existing furniture keeps its local IDs and SQL settings; new furniture receives a free local ID when the source ID is occupied.",
|
||||
"sqlRecords": "SQL records and catalog offers",
|
||||
"noSqlItem": "No local SQL item.",
|
||||
"itemRow": "Item #{id} · Sprite #{sprite} · {name}",
|
||||
"offerRow": "Offer #{id} · Page #{page} · {credits} credits + {points} points",
|
||||
"healthComplete": "Complete",
|
||||
"healthIncomplete": "Needs repair",
|
||||
"healthNew": "Not imported",
|
||||
"healthConflict": "Local conflict",
|
||||
"healthUnknown": "Not verified",
|
||||
"missing": "Missing: {parts}",
|
||||
"conflictNote": "Duplicate records or mismatched IDs/types require review before repair.",
|
||||
"unknownNote": "Some local data could not be checked. Retry verification before repair.",
|
||||
"repairNote": "Repair adds missing parts and preserves existing IDs, settings, furnidata and catalog offers. Missing offers use the automatic category and price shown below.",
|
||||
"repairQueued": "Repair queued or running…",
|
||||
"repair": "Repair furni",
|
||||
"recheck": "Recheck local state",
|
||||
"checking": "Checking local data and assets…",
|
||||
"retry": "Retry inspection",
|
||||
"unavailable": "Local inspection unavailable."
|
||||
},
|
||||
"quality": {
|
||||
"compressionTitle": "Compression · {format}",
|
||||
"losslessVp8l": "Lossless (VP8L)",
|
||||
"lossyVp8": "Lossy (VP8)",
|
||||
"bundle": "Bundle",
|
||||
"texture": "Texture",
|
||||
"metadata": "Metadata",
|
||||
"container": "Container",
|
||||
"textureDetail": "Texture {raw} decoded · deflate ×{ratio} · sheet {width}×{height} · {frames} frames",
|
||||
"legacyPngSaving": "Legacy PNG texture. Re-importing as WebP Lossless would store <strong>{size}</strong>, saving <strong>{saving}</strong> ({percent}% smaller)",
|
||||
"legacyPngNoGain": "Legacy PNG texture. Re-importing as WebP Lossless would store <strong>{size}</strong> (no gain on this bundle)",
|
||||
"webpSaving": "WebP Lossless stores {raw} against {png} for the same artwork as PNG, saving <strong>{saving}</strong> ({percent}% smaller)",
|
||||
"webpNoGain": "WebP Lossless stores {raw} against {png} for the same artwork as PNG (PNG is not larger on this bundle)",
|
||||
"pixelExact": "<strong>Pixel-exact</strong> — every visible texel round-trips unchanged.",
|
||||
"invisibleDiffs": "{count} fully transparent texels carry no RGB, which VP8L discards and no renderer can see.",
|
||||
"texelsDiffer": "{count} {count, plural, one {texel} other {texels}} differ after a lossless round-trip — the furniture may not render identically.",
|
||||
"scaleTitle": "Scale {size} · {state}",
|
||||
"stateMissing": "Not included",
|
||||
"stateIssues": "Needs review",
|
||||
"stateDerived": "Derived from 64",
|
||||
"statePresent": "Present",
|
||||
"spritesLine": "{sprites} sprites · {animations} animation definitions · directions {directions}",
|
||||
"missingScale": "This bundle contains no sprites at this scale. Missing scale 32 does not by itself mean the furniture is unusable.",
|
||||
"sprite": "Sprite",
|
||||
"renderFailed": "This sprite could not be rendered.",
|
||||
"intro": "Inspect the saved bundle at both scales. Previews show individual sprites, not the assembled furniture or animation playback.",
|
||||
"recheck": "Recheck",
|
||||
"checking": "Checking scales and texture references…",
|
||||
"inspectionFailed": "Inspection failed"
|
||||
},
|
||||
"sync": {
|
||||
"pageSubtitle": "Keep the catalog in sync with your sources and export it to the repository.",
|
||||
"cloneSourcesTitle": "Clone sources",
|
||||
"cloneSourcesBody": "Scan all configured clone sources and automatically import any furniture that is not yet present in your hotel.",
|
||||
"officialFurnidataTitle": "Official furnidata",
|
||||
"officialFurnidataBody": "Scan the official Habbo furnidata and automatically import any furniture that your hotel doesn't have yet.",
|
||||
"gitTitle": "Gitea connection settings",
|
||||
"gitIntro": "Connect your self-hosted Gitea repository over HTTPS. The CMS manages its own checkout; no Git-specific .env settings are needed.",
|
||||
"providerLabel": "Provider:",
|
||||
"providerValue": "Gitea self-hosted",
|
||||
"repositoryUrl": "Repository URL",
|
||||
"branch": "Branch",
|
||||
"username": "Gitea username",
|
||||
"accessToken": "Gitea access token",
|
||||
"tokenSaved": "Token saved — leave blank to keep it",
|
||||
"tokenEnter": "Enter a token with repository write access",
|
||||
"tokenNote": "The token is stored encrypted and is never returned to the browser. Changing repository or username requires entering the token again.",
|
||||
"autoExport": "Automatically export catalog changes",
|
||||
"testConnection": "Test connection",
|
||||
"working": "Working…",
|
||||
"saveSettings": "Save Gitea settings",
|
||||
"loadFailed": "Unable to read Git settings. Settings edit permission is required.",
|
||||
"requestFailed": "Git configuration request failed",
|
||||
"saved": "Gitea configuration saved."
|
||||
},
|
||||
"scale32": {
|
||||
"title": "Generate scale 32 from 64",
|
||||
"intro": "Adds derived artwork using nearest-neighbor reduction. Existing scale 32 is preserved. Preview uses the saved bundle and shows one sprite at its original pixel size.",
|
||||
"saveFirst": "Save or reset your pending edits first.",
|
||||
"working": "Working…",
|
||||
"preview": "Preview generation",
|
||||
"previewCount": "{count} sprite entries will be added. Check positioning and animations in the client after saving.",
|
||||
"skipped": "{count} sprite(s) were skipped because the source bundle has no usable artwork for them.",
|
||||
"original64": "Original 64",
|
||||
"derived32": "Derived 32",
|
||||
"saveWithBackup": "Save with backup",
|
||||
"discard": "Discard preview",
|
||||
"saved": "Saved. A copy of the original bundle is preserved.",
|
||||
"restore": "Restore original bundle",
|
||||
"generationFailed": "Generation failed"
|
||||
},
|
||||
"pages": {
|
||||
"auditSubtitle": "Scan your entire furniture catalog for missing entries, broken references, missing files, and items available from clone sources.",
|
||||
"badgesSubtitle": "Search hotels and import badge designs into your hotel.",
|
||||
"cloneSubtitle": "Clone furniture from your configured external sources.",
|
||||
"clothingSubtitle": "Browse and import wearable clothing assets for your hotel.",
|
||||
"effectsSubtitle": "Import effect definitions and their sprite assets.",
|
||||
"maintenanceSubtitle": "Detect and repair catalog integrity problems, and run legacy maintenance tasks.",
|
||||
"nitroCleanupSubtitle": "Scan the nitro directories for fake bundles (no matching furniture) and broken bundles (unparseable) and remove them.",
|
||||
"petsSubtitle": "Import pet types, races and their sprite assets.",
|
||||
"repairIconsTitle": "Repair missing icons",
|
||||
"repairIconsSubtitle": "Scan all existing furniture and download any missing icon files. Icons are extracted from local .nitro bundles or fetched from configured clone sources.",
|
||||
"uploadTitle": "Upload .hab furniture",
|
||||
"uploadSubtitle": "Upload a .hab bundle and optional icon to import furniture into the hotel. The database entry, catalog item, and FurnitureData.json will be updated automatically."
|
||||
},
|
||||
"confirm": {
|
||||
"deleteTitle": "Delete {count} items?",
|
||||
"deleteDescription": "All {count} selected imported items will be permanently removed from the database and the filesystem.",
|
||||
"deleteConfirm": "Delete"
|
||||
},
|
||||
"batch": {
|
||||
"ariaProgress": "Import progress",
|
||||
"ariaProcessed": "Furniture processed",
|
||||
"headingStopping": "Stopping import…",
|
||||
"headingImporting": "Importing furniture…",
|
||||
"headingStopped": "Import stopped",
|
||||
"headingCompletedErrors": "Import completed with errors",
|
||||
"headingCompleted": "Import completed",
|
||||
"imported": "imported",
|
||||
"failed": "failed",
|
||||
"notCompleted": "not completed",
|
||||
"remaining": "remaining",
|
||||
"retryFailed": "Retry failed ({count})",
|
||||
"closeSummary": "Close summary",
|
||||
"stopImport": "Stop import",
|
||||
"verificationIssues": "{count, plural, one {# catalog data issue still needs attention. Open Catalog audit to review it.} other {# catalog data issues still need attention. Open Catalog audit to review them.}}",
|
||||
"verificationDone": "Catalog data verification completed.",
|
||||
"chipFurnidataIds": "furnidata ids fixed",
|
||||
"chipOfferIds": "offer ids reconciled",
|
||||
"chipOwnerships": "ownerships fixed",
|
||||
"chipNitros": "nitros synced",
|
||||
"chipIcons": "icons synced",
|
||||
"viewItemDetails": "View item details ({count})",
|
||||
"showingItems": "Showing {shown} of {total} items",
|
||||
"status": {
|
||||
"pending": "Waiting",
|
||||
"started": "Starting",
|
||||
"downloading": "Downloading assets",
|
||||
"converting": "Preparing assets",
|
||||
"writingDb": "Saving",
|
||||
"done": "Imported",
|
||||
"failed": "Failed"
|
||||
},
|
||||
"statusNotCompleted": "Not completed"
|
||||
},
|
||||
"toasts": {
|
||||
"allImported": "All items from this source are already imported",
|
||||
"selectNotImported": "Select furni that isn't imported yet",
|
||||
"loadMissingFailed": "Could not load missing furniture",
|
||||
"prepareFailed": "Could not prepare the full selection",
|
||||
"sourceChanged": "The source changed while preparing the selection. Refresh and retry.",
|
||||
"batchFailed": "Batch import failed ({status})",
|
||||
"importCancelled": "Import cancelled",
|
||||
"importTimeout": "Import timed out. Progress was saved — open Import History to resume failed items.",
|
||||
"networkUpload": "Network error during upload. Progress was saved — open Import History to resume failed items.",
|
||||
"unexpectedImport": "Unexpected import error: {message}",
|
||||
"retryFailed": "Retry failed ({status})",
|
||||
"importedAgain": "{count} items imported again",
|
||||
"retryConnection": "Connection failed while retrying",
|
||||
"deleteFailed": "Delete failed",
|
||||
"networkError": "Network error",
|
||||
"deleted": "Deleted {name} ({count} files)",
|
||||
"regenerated": "Nitro file regenerated for {name}",
|
||||
"regenerationFailed": "Regeneration failed",
|
||||
"bulkDeleted": "Deleted: {count}",
|
||||
"bulkDeletedFailed": "Deleted: {deleted}, failed: {failed}",
|
||||
"bulkDeleteFailed": "Bulk delete failed",
|
||||
"regeneratedBulk": "Nitro regenerated: {count}",
|
||||
"regeneratedBulkFailed": "Nitro regenerated: {succeeded}, failed: {failed}",
|
||||
"furnidataAdded": "Furnidata added: {count}",
|
||||
"furnidataAddedFailed": "Furnidata added: {fixed}, failed: {failed}",
|
||||
"addFurnidataFailed": "Adding furnidata failed",
|
||||
"languagesTranslated": "Languages translated ({ok}/{total} completed)",
|
||||
"translationFailed": "Translation failed",
|
||||
"networkTranslating": "Network error while translating languages",
|
||||
"saveDefaultFailed": "Could not save the default translation setting"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user