fix(i18n): persist CMS translations and validate message catalogs
This commit is contained in:
1 parent
96234075f8
commit
ef94646d60
37 files changed
+1621
-463
No files matched your search
+268
-32
@@ -878,7 +878,8 @@
|
||||
"codePlaceholder": "6-digit code",
|
||||
"confirm": "Confirm",
|
||||
"enableIntro": "Add a second layer of security with an authenticator app.",
|
||||
"enable": "Enable 2FA"
|
||||
"enable": "Enable 2FA",
|
||||
"rateLimit": "Too many attempts. Please wait before trying again."
|
||||
},
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
@@ -1119,7 +1120,8 @@
|
||||
"rankWithCount": "Rank {rank}: {count} accounts",
|
||||
"recentStaffActivity": "Recent staff activity",
|
||||
"viewAll": "View all",
|
||||
"noStaffActivityLogged": "No staff activity logged yet"
|
||||
"noStaffActivityLogged": "No staff activity logged yet",
|
||||
"totalAccounts": "{count} total accounts"
|
||||
},
|
||||
"articles": {
|
||||
"title": "Articles",
|
||||
@@ -1160,7 +1162,11 @@
|
||||
"statusPublished": "Published",
|
||||
"publishAt": "Publish at",
|
||||
"publishedAt": "Published at"
|
||||
}
|
||||
},
|
||||
"articlesShown": "Articles shown",
|
||||
"latest": "Latest article",
|
||||
"mostRecentPost": "Most recent publication",
|
||||
"recentArticles": "Recent articles"
|
||||
},
|
||||
"events": {
|
||||
"newEvent": "+ New event",
|
||||
@@ -1281,7 +1287,13 @@
|
||||
"colColour": "Colour",
|
||||
"colUsedBy": "Used by",
|
||||
"colCreated": "Created",
|
||||
"colActions": "Actions"
|
||||
"colActions": "Actions",
|
||||
"databaseError": "Tags could not be loaded. Please try again.",
|
||||
"totalTags": "Total tags",
|
||||
"inUse": "In use",
|
||||
"attachedToContent": "Attached to content",
|
||||
"unused": "Unused",
|
||||
"allTags": "All tags"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigator",
|
||||
@@ -1305,7 +1317,12 @@
|
||||
"colName": "Name",
|
||||
"colOrder": "Order",
|
||||
"colRooms": "Rooms",
|
||||
"colActions": "Actions"
|
||||
"colActions": "Actions",
|
||||
"publicFlatcats": "Public room categories",
|
||||
"markedPublic": "Categories marked as public",
|
||||
"visibleRooms": "Visible rooms",
|
||||
"rows": "rows",
|
||||
"unnamed": "Unnamed room"
|
||||
},
|
||||
"helpCenter": {
|
||||
"title": "Help Center",
|
||||
@@ -1332,7 +1349,9 @@
|
||||
"saving": "Saving…",
|
||||
"deleteEntry": "Delete entry",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
},
|
||||
"withButton": "With a button",
|
||||
"smallBoxes": "Small boxes"
|
||||
},
|
||||
"ads": {
|
||||
"title": "Advertisements",
|
||||
@@ -1394,8 +1413,17 @@
|
||||
"save": "Save box",
|
||||
"saving": "Saving…",
|
||||
"deleteBox": "Delete box",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
"cancel": "Cancel",
|
||||
"iconEmoji": "Icon or emoji"
|
||||
},
|
||||
"totalBoxes": "Total boxes",
|
||||
"active": "Active",
|
||||
"shownOnHomePage": "Shown on the home page",
|
||||
"position": "Position",
|
||||
"boxes": "Content boxes",
|
||||
"hidden": "Hidden",
|
||||
"hide": "Hide",
|
||||
"activate": "Activate"
|
||||
},
|
||||
"photos": {
|
||||
"title": "Photo Moderation",
|
||||
@@ -1522,7 +1550,20 @@
|
||||
"save": "Save ban",
|
||||
"saving": "Saving…",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
},
|
||||
"activeBans": "Active bans",
|
||||
"permanent": "Permanent bans",
|
||||
"accountBans": "Account bans",
|
||||
"userIdPlaceholder": "User ID",
|
||||
"typeAccount": "Account",
|
||||
"typeIp": "IP address",
|
||||
"typeMachine": "Device",
|
||||
"typeSuper": "Account, IP and device",
|
||||
"hoursPlaceholder": "Duration in hours",
|
||||
"reasonPlaceholder": "Reason for the ban",
|
||||
"ban": "Ban user",
|
||||
"colUserId": "User ID",
|
||||
"lift": "Lift ban"
|
||||
},
|
||||
"ip": {
|
||||
"title": "IP Management",
|
||||
@@ -1542,7 +1583,11 @@
|
||||
"colIp": "IP",
|
||||
"colReason": "Reason",
|
||||
"colAdded": "Added",
|
||||
"colActions": "Actions"
|
||||
"colActions": "Actions",
|
||||
"whitelisted": "Allowed IP addresses",
|
||||
"blacklisted": "Blocked IP addresses",
|
||||
"asnPlaceholder": "ASN (optional)",
|
||||
"colAsn": "ASN"
|
||||
},
|
||||
"vpn": {
|
||||
"title": "VPN / Proxy Detection",
|
||||
@@ -1555,7 +1600,21 @@
|
||||
"blockOnDetectHint": "Automatically block users detected behind a VPN or proxy",
|
||||
"save": "Save settings",
|
||||
"saving": "Saving…",
|
||||
"settingsSaved": "VPN settings saved successfully"
|
||||
"settingsSaved": "VPN settings saved successfully",
|
||||
"protection": "VPN protection",
|
||||
"on": "Enabled",
|
||||
"off": "Disabled",
|
||||
"providerLabel": "Provider: {provider}",
|
||||
"notScreened": "Connections are not checked for VPN use.",
|
||||
"settings": "VPN settings",
|
||||
"provider": "Provider",
|
||||
"providerHint": "Choose the service used to check incoming connections.",
|
||||
"blockMessage": "Blocked connection message",
|
||||
"blockMessagePlaceholder": "Please disable your VPN and try again.",
|
||||
"blockMessageHint": "Shown when a connection is blocked by VPN protection.",
|
||||
"rawIpTitle": "IP address lists",
|
||||
"rawIpDescription": "Manage individual addresses in <ipLink>IP management</ipLink>.",
|
||||
"openIpManagement": "Open IP management"
|
||||
},
|
||||
"tickets": {
|
||||
"title": "CMS support desk",
|
||||
@@ -1685,7 +1744,10 @@
|
||||
"status": "Status",
|
||||
"submitted": "Submitted",
|
||||
"noMessage": "No message provided"
|
||||
}
|
||||
},
|
||||
"applicationsTitle": "Applications ({count})",
|
||||
"userNumber": "User #{id}",
|
||||
"rankLabel": "Rank {rank}"
|
||||
},
|
||||
"teams": {
|
||||
"title": "Teams",
|
||||
@@ -1713,7 +1775,26 @@
|
||||
"save": "Save team definition",
|
||||
"saving": "Saving…",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
},
|
||||
"teamRanks": "Staff ranks",
|
||||
"visible": "Visible",
|
||||
"hidden": "Hidden",
|
||||
"newTeamRank": "New staff rank",
|
||||
"rankName": "Rank name",
|
||||
"rankNamePlaceholder": "For example: Moderator",
|
||||
"badgeCode": "Badge code",
|
||||
"badgeCodePlaceholder": "For example: ADM",
|
||||
"jobDescription": "Job description",
|
||||
"jobDescriptionPlaceholder": "Describe this staff role",
|
||||
"staffColor": "Staff colour",
|
||||
"staffColorPlaceholder": "For example: #3366cc",
|
||||
"hiddenRank": "Hide from the staff page",
|
||||
"createTeam": "Create staff rank",
|
||||
"ranksTitle": "Staff ranks ({count})",
|
||||
"badge": "Badge",
|
||||
"color": "Colour",
|
||||
"visibility": "Visibility",
|
||||
"delete": "Delete"
|
||||
},
|
||||
"permissions": {
|
||||
"title": "Permissions",
|
||||
@@ -1888,8 +1969,23 @@
|
||||
"enabled": "Enabled",
|
||||
"save": "Save package",
|
||||
"saving": "Saving…",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
"cancel": "Cancel",
|
||||
"infoPlaceholder": "Describe the package contents",
|
||||
"iconPlaceholder": "Icon URL",
|
||||
"colorPlaceholder": "For example: #3366cc",
|
||||
"costsPlaceholder": "Price",
|
||||
"creditsPlaceholder": "Number of credits",
|
||||
"ducketsPlaceholder": "Number of duckets",
|
||||
"diamondsPlaceholder": "Number of diamonds",
|
||||
"giveRankPlaceholder": "Rank ID (0 for none)",
|
||||
"badgesPlaceholder": "Badge codes separated by semicolons",
|
||||
"positionPlaceholder": "Display order",
|
||||
"create": "Create package",
|
||||
"deletePackage": "Delete package"
|
||||
},
|
||||
"newPackageTitle": "New shop package",
|
||||
"backToShop": "Back to shop",
|
||||
"editPackageTitle": "Edit shop package"
|
||||
},
|
||||
"marketplace": {
|
||||
"title": "Marketplace",
|
||||
@@ -2056,7 +2152,15 @@
|
||||
"colActions": "Actions",
|
||||
"statusActive": "Active",
|
||||
"statusCancelled": "Cancelled",
|
||||
"statusExpired": "Expired"
|
||||
"statusExpired": "Expired",
|
||||
"activeSubs": "Active subscriptions",
|
||||
"types": "Subscription types",
|
||||
"expiringSoon": "Expiring soon",
|
||||
"activeSubscriptions": "Active subscriptions",
|
||||
"colSubId": "Subscription ID",
|
||||
"colUserId": "User ID",
|
||||
"colType": "Type",
|
||||
"colExpires": "Expires"
|
||||
},
|
||||
"rareValues": {
|
||||
"title": "Rare Values",
|
||||
@@ -2095,8 +2199,24 @@
|
||||
"pointTypeDiamonds": "Diamonds",
|
||||
"save": "Save item",
|
||||
"saving": "Saving…",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
"cancel": "Cancel",
|
||||
"itemIdPlaceholder": "Item ID",
|
||||
"iconPlaceholder": "Icon URL",
|
||||
"creditsPlaceholder": "Credits",
|
||||
"currencyValuePlaceholder": "Currency value",
|
||||
"addValue": "Add value"
|
||||
},
|
||||
"badgePlaceholder": "Category badge code",
|
||||
"priorityPlaceholder": "Priority",
|
||||
"addCategoryButton": "Add category",
|
||||
"badgeLabel": "Badge: {badge}",
|
||||
"priorityLabel": "Priority: {priority}",
|
||||
"itemCount": "{count} items",
|
||||
"colItemId": "Item ID",
|
||||
"colCurrencyValue": "Currency value",
|
||||
"colCurrency": "Currency",
|
||||
"colIcon": "Icon",
|
||||
"noValuesInCategory": "No values in this category yet."
|
||||
},
|
||||
"badges": {
|
||||
"title": "Badges",
|
||||
@@ -2127,7 +2247,10 @@
|
||||
"colImage": "Image",
|
||||
"colName": "Name",
|
||||
"colDescription": "Description",
|
||||
"colActions": "Actions"
|
||||
"colActions": "Actions",
|
||||
"uploadedMessage": "Badge {code} uploaded.",
|
||||
"errorMessage": "Upload failed: {error}",
|
||||
"userIdPlaceholder": "User ID"
|
||||
},
|
||||
"achievements": {
|
||||
"title": "Achievements",
|
||||
@@ -2141,7 +2264,14 @@
|
||||
"colProgress": "Progress",
|
||||
"levelLabel": "Level {level}",
|
||||
"rewardCredits": "{count} credits",
|
||||
"rewardPoints": "{count} {points}"
|
||||
"rewardPoints": "{count} {points}",
|
||||
"achievementRows": "Achievement levels",
|
||||
"distinctAchievements": "Distinct achievements",
|
||||
"categories": "Categories",
|
||||
"colLevel": "Level",
|
||||
"colRewardType": "Reward type",
|
||||
"colRewardAmount": "Reward amount",
|
||||
"colPoints": "Points"
|
||||
},
|
||||
"calendar": {
|
||||
"title": "Calendar campaigns",
|
||||
@@ -2160,8 +2290,32 @@
|
||||
"days": "Days",
|
||||
"dayLabel": "Day {day}",
|
||||
"reward": "Reward",
|
||||
"noDays": "No days configured for this campaign"
|
||||
}
|
||||
"noDays": "No days configured for this campaign",
|
||||
"campaignMeta": "#{id} · {days} days · starts {date}",
|
||||
"disabled": "Disabled",
|
||||
"enabled": "Enabled",
|
||||
"locksExpired": "Expired days are locked",
|
||||
"rewards": "Rewards",
|
||||
"noRewards": "No rewards configured.",
|
||||
"colId": "ID",
|
||||
"colProduct": "Product",
|
||||
"colItem": "Item",
|
||||
"colCredits": "Credits",
|
||||
"colPixels": "Duckets",
|
||||
"colPoints": "Points",
|
||||
"colBadge": "Badge",
|
||||
"colSubscription": "Subscription",
|
||||
"typeLabel": "Type {type}",
|
||||
"rewardCount": "{count} rewards"
|
||||
},
|
||||
"campaigns": "Campaigns",
|
||||
"enabled": "Enabled campaigns",
|
||||
"totalRewards": "Total rewards",
|
||||
"statusEnabled": "Enabled",
|
||||
"statusDisabled": "Disabled",
|
||||
"campaignMeta": "#{id} · {days} days · {rewards} rewards",
|
||||
"startsAt": "Starts: {date}",
|
||||
"locksExpired": "Expired days are locked"
|
||||
},
|
||||
"radio": {
|
||||
"title": "Radio Moderation",
|
||||
@@ -2231,7 +2385,7 @@
|
||||
"noStreamUrlText": "There is no radio_stream_url set, so there is nothing to embed yet.",
|
||||
"goToSettings": "Go to Radio Settings",
|
||||
"livePreview": "Live preview",
|
||||
"livePreviewText": "This is the bare <audio> player as visitors will see it. Press play to confirm the stream is reachable.",
|
||||
"livePreviewText": "This is the bare audio player as visitors will see it. Press play to confirm the stream is reachable.",
|
||||
"source": "Source",
|
||||
"audioSnippet": "Audio player snippet",
|
||||
"audioSnippetDesc": "Drop-in HTML5 audio player pointed straight at the stream. Works anywhere plain HTML is allowed.",
|
||||
@@ -2355,7 +2509,8 @@
|
||||
"pointsPerMinuteHint": "Amount awarded for each full minute of listening.",
|
||||
"maxPerDayHint": "Daily cap per user. Use 0 for unlimited.",
|
||||
"minListenersHint": "Points are only awarded when at least this many people are tuned in.",
|
||||
"save": "Save points settings"
|
||||
"save": "Save points settings",
|
||||
"saved": "Settings saved."
|
||||
},
|
||||
"autoDjForm": {
|
||||
"title": "Radio AutoDJ",
|
||||
@@ -2408,7 +2563,7 @@
|
||||
"noNowPlayingHint": "no now-playing API set",
|
||||
"noListenersHint": "no listeners API set",
|
||||
"endpointDiagnostics": "Endpoint diagnostics",
|
||||
"diagnosticsLegend": "<span class=\"inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-green-600/20 text-[var(--color-text)]\">OK</span> reachable · <span class=\"inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]\">WARN</span> not configured · <span class=\"inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-red-500/22 text-[var(--color-text)]\">ERROR</span> fetch failed / timed out.",
|
||||
"diagnosticsLegend": "OK: reachable · WARN: not configured · ERROR: request failed or timed out.",
|
||||
"streamUrl": "Stream URL",
|
||||
"unreachable": "unreachable",
|
||||
"refresh": "Refresh"
|
||||
@@ -2589,7 +2744,16 @@
|
||||
"valuePlaceholder": "Enter value…",
|
||||
"save": "Save changes",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
},
|
||||
"settingsKeys": "Settings",
|
||||
"textKeys": "Text entries",
|
||||
"keysCount": "{count} keys",
|
||||
"addOverwriteSetting": "Add or update a setting",
|
||||
"key": "Key",
|
||||
"value": "Value",
|
||||
"showingFirst": "Showing the first {count}",
|
||||
"addOverwriteText": "Add or update a text",
|
||||
"truncatedHint": "Showing {shown} of {total} entries."
|
||||
},
|
||||
"alerts": {
|
||||
"title": "Alerts",
|
||||
@@ -2857,7 +3021,25 @@
|
||||
"save": "Save settings",
|
||||
"saving": "Saving…",
|
||||
"confirmEnable": "Enabling maintenance will prevent all non-staff users from accessing the hotel. Continue?",
|
||||
"confirmDisable": "Disable maintenance mode and restore access for all users?"
|
||||
"confirmDisable": "Disable maintenance mode and restore access for all users?",
|
||||
"maintenance": "Maintenance",
|
||||
"hotelLockedDown": "Access to the hotel is restricted.",
|
||||
"hotelOpen": "The hotel is open.",
|
||||
"minLoginRank": "Minimum login rank",
|
||||
"duringMaintenance": "During maintenance",
|
||||
"splashMessage": "Maintenance message",
|
||||
"set": "Configured",
|
||||
"empty": "Empty",
|
||||
"currentState": "Current status",
|
||||
"maintenanceCurrently": "Maintenance: {status}",
|
||||
"onlyRankCanLogin": "Only users with rank {rank} or higher can log in.",
|
||||
"hotelOpenToEveryone": "All eligible users can log in.",
|
||||
"settings": "Settings",
|
||||
"enableMaintenance": "Enable maintenance mode",
|
||||
"minLoginRankLabel": "Minimum rank allowed during maintenance",
|
||||
"maintenanceMessageLabel": "Message shown to visitors",
|
||||
"maintenanceMessagePlaceholder": "Explain why the hotel is unavailable.",
|
||||
"saveChanges": "Save changes"
|
||||
},
|
||||
"housekeeping": {
|
||||
"title": "Permission archive",
|
||||
@@ -2941,7 +3123,7 @@
|
||||
"tabTest": "Test",
|
||||
"tabAudit": "Audit log",
|
||||
"importPermissions": "Import permissions",
|
||||
"importHint": "Paste a JSON array of permission objects. Format: [{ \"permission\": \"...\", \"minRank\": 7, \"description\": \"...\", \"groupName\": \"...\", \"dependsOn\": \"...\" }]",
|
||||
"importHint": "Paste a JSON array of permission objects with the fields permission, minRank, description, groupName and dependsOn.",
|
||||
"importPlaceholder": "Paste JSON here…",
|
||||
"import": "Import",
|
||||
"exportTitle": "Export permissions",
|
||||
@@ -3058,8 +3240,13 @@
|
||||
"variablesHint": "{username}, {email}, {hotel_name}",
|
||||
"save": "Save template",
|
||||
"saving": "Saving…",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
"cancel": "Cancel",
|
||||
"variablesPlaceholder": "Variables, separated by commas",
|
||||
"active": "Active"
|
||||
},
|
||||
"create": "Create template",
|
||||
"save": "Save template",
|
||||
"updated": "Updated: {date}"
|
||||
},
|
||||
"theme": {
|
||||
"title": "Theme",
|
||||
@@ -3085,7 +3272,12 @@
|
||||
"saveCss": "Save CSS",
|
||||
"savingCss": "Saving…",
|
||||
"reset": "Reset to defaults",
|
||||
"confirmReset": "Reset theme to default values?"
|
||||
"confirmReset": "Reset theme to default values?",
|
||||
"saved": "Theme settings saved.",
|
||||
"presetApplied": "Preset applied: {preset}",
|
||||
"typographyLayout": "Typography and layout",
|
||||
"font": "Font",
|
||||
"borderRadius": "Corner radius"
|
||||
},
|
||||
"media": {
|
||||
"title": "Media Library",
|
||||
@@ -3170,6 +3362,43 @@
|
||||
"subtitle": "User-facing strings from emulator_settings (commands, wordfilter, pet/matchmaking messages).",
|
||||
"infoBox": "Configuration settings (ports, timeouts, IDs) do not appear here — use the full settings link. Saved changes are synced to the emulator via RCON.",
|
||||
"fullSettings": "Full settings"
|
||||
},
|
||||
"cms": {
|
||||
"title": "CMS translations",
|
||||
"description": "Edit the text shown on the website and in housekeeping. Saved changes are applied on the next page load and survive deployments.",
|
||||
"export": "Export JSON",
|
||||
"saving": "Saving…",
|
||||
"saveLocale": "Save {locale}",
|
||||
"saved": "Translations saved and applied.",
|
||||
"readOnly": "You can review and export translations. Editing requires permission to change settings.",
|
||||
"language": "Language",
|
||||
"coverage": "{total} keys · {missing} missing · {identical} identical to English",
|
||||
"search": "Search",
|
||||
"searchPlaceholder": "Key, translation or English source…",
|
||||
"group": "Group",
|
||||
"allGroups": "All groups",
|
||||
"status": "Show",
|
||||
"all": "All texts",
|
||||
"missing": "Missing translations",
|
||||
"identical": "Identical to English",
|
||||
"modified": "Unsaved changes",
|
||||
"fallbackHint": "Missing translations use English. Identical text may be intentional. Keep variable names inside braces unchanged.",
|
||||
"unsaved": "{count} unsaved changes in this language",
|
||||
"discard": "Discard changes",
|
||||
"discardConfirm": "Discard unsaved changes in this language?",
|
||||
"fixErrors": "Fix {count} invalid messages before saving.",
|
||||
"noResults": "No texts match these filters.",
|
||||
"source": "English reference",
|
||||
"missingPlaceholder": "Translation missing; English is used.",
|
||||
"page": "Page {page} of {total} · {count} keys",
|
||||
"previous": "Previous",
|
||||
"next": "Next",
|
||||
"leaveWarning": "You have unsaved translations. Leave this page?",
|
||||
"conflict": "These translations changed since you opened the editor. Export your changes, then reload before saving.",
|
||||
"unknownKey": "Unknown translation key: {key}",
|
||||
"empty": "Enter a translation for {key}, or discard this change.",
|
||||
"invalid": "Invalid message syntax in {key}. Check braces and rich-text tags.",
|
||||
"variables": "Keep the same variable names and rich-text tags as the English source in {key}."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@@ -3299,7 +3528,14 @@
|
||||
"pages": "Pages and tools",
|
||||
"description": "Find an admin page or search hotel records.",
|
||||
"loadError": "Records could not be loaded. You can still open the pages above.",
|
||||
"placeholder": "Search pages, users, rooms…"
|
||||
"placeholder": "Search pages, users, rooms…",
|
||||
"users": "Users",
|
||||
"articles": "Articles",
|
||||
"rooms": "Rooms",
|
||||
"guilds": "Groups",
|
||||
"shop": "Shop",
|
||||
"rareValues": "Rare values",
|
||||
"noResults": "No results found."
|
||||
},
|
||||
"table": {
|
||||
"search": "Search…",
|
||||
|
||||
Reference in new issue
Block a user