282 lines
12 KiB
TypeScript
282 lines
12 KiB
TypeScript
import type { MessageKey } from "./zh-CN.js";
|
|
|
|
/** 与 zh-CN 同构;类型强制 key 对齐。 */
|
|
export const en: Record<MessageKey, string> = {
|
|
"meta.brand": "Fanmu",
|
|
"meta.product": "Fanmu",
|
|
"meta.productTagline": "Local files · online danmaku",
|
|
"meta.tagline": "Local video · online danmaku",
|
|
|
|
"nav.home": "Home",
|
|
"nav.dashboard": "Console",
|
|
"nav.primary": "Primary nav",
|
|
"nav.brandHome": "Home",
|
|
"nav.library": "Library",
|
|
"nav.browse": "Browse",
|
|
"nav.watch": "Watch",
|
|
"nav.mounts": "Mounts",
|
|
"nav.danmaku": "Danmaku settings",
|
|
|
|
"home.title": "Fanmu",
|
|
"home.subtitle":
|
|
"Mount WebDAV / OpenList libraries, scan into your media library, play with ArtPlayer and online danmaku.",
|
|
"home.heroLine1": "Local files",
|
|
"home.heroLine2": "Online danmaku",
|
|
"home.ctaDashboard": "Open library",
|
|
"home.ctaLogin": "Sign in / Register",
|
|
"home.stackTitle": "Features",
|
|
"home.stackBody":
|
|
"WebDAV mounts · Bangumi scraping · ArtPlayer + danmaku · multi-user progress · media dashboard.",
|
|
"home.layerTitle": "Get started",
|
|
"home.layerBody":
|
|
"Register → add a WebDAV mount → scan in Browse → play from the Library with danmaku.",
|
|
"home.footer": "Private library · Danmaku",
|
|
|
|
"dashboard.title": "Console",
|
|
"dashboard.heading": "Media hub",
|
|
"dashboard.label": "Console",
|
|
"dashboard.desc":
|
|
"Redirecting to the library… If nothing happens, open Library in the sidebar.",
|
|
|
|
"library.title": "Library",
|
|
"library.scan": "Scan mounts",
|
|
"library.unmatched": "Unmatched",
|
|
"library.continue": "Continue watching",
|
|
"library.searchPlaceholder": "Search title",
|
|
"library.filterAll": "All",
|
|
"library.filterOk": "Scraped",
|
|
"library.noPoster": "No poster",
|
|
"library.play": "Play",
|
|
"library.match": "Match",
|
|
"library.empty": "No media yet. Scan a mount in the file browser first.",
|
|
"library.loadMore": "Load more",
|
|
"library.ctaBrowse": "Open file browser",
|
|
"library.ctaMounts": "Add mount",
|
|
"library.matchTitle": "Manual Bangumi match",
|
|
"library.searchWork": "Search by title",
|
|
"library.bind": "Bind",
|
|
"library.cancel": "Cancel",
|
|
"library.viewWall": "Poster wall",
|
|
"library.viewList": "List",
|
|
"library.episodeCount": "{count} eps",
|
|
"library.groupsEmpty":
|
|
"No scraped titles yet. Scan a mount or match manually to see them here.",
|
|
"library.episodes": "Episodes",
|
|
"library.expand": "Show episodes",
|
|
"library.collapse": "Collapse",
|
|
"library.epLabel": "Episode",
|
|
"library.epPlaceholder": "Optional, e.g. 12",
|
|
"library.reScrape": "Re-scrape",
|
|
"library.manualTitle": "Manual match",
|
|
"library.candidates": "Candidates (click to bind)",
|
|
"browse.batchScrape": "Batch scrape folder",
|
|
"browse.batchBind": "Bind folder as title",
|
|
"browse.batchDone": "{message}",
|
|
"browse.batchError": "Batch action failed",
|
|
"browse.folderActions": "Folder actions",
|
|
"browse.bindFolderTitle": "Bind folder to a title",
|
|
"browse.bindFolderHint":
|
|
"Search and pick a title; all videos under this folder will bind to it, episodes parsed from filenames.",
|
|
"library.backToList": "Back to library",
|
|
"library.continueTitle": "Continue watching",
|
|
"library.matched": "Matched",
|
|
"library.autoScanning": "Auto-scanning mounted directories into the library…",
|
|
"library.noResults": "No results matching “{q}”",
|
|
"library.filterEmpty": "Nothing under the current filter — try another filter",
|
|
"library.clearSearch": "Clear search",
|
|
|
|
"browse.title": "File browser",
|
|
"browse.play": "Play",
|
|
"browse.addLibrary": "Scan into library",
|
|
"browse.selectMount": "Select mount…",
|
|
"browse.mountsEmpty": "No mounts yet",
|
|
"browse.root": "Root",
|
|
"browse.up": "Up",
|
|
"browse.empty": "Empty directory or no mount selected",
|
|
"browse.addMount": "Add mount",
|
|
"browse.loading": "Loading…",
|
|
"browse.loadError": "Failed to read directory",
|
|
"browse.playError": "Failed to add to library",
|
|
"browse.scanDone": "Scan complete: {count} files",
|
|
"browse.scanError": "Scan failed. Check the mount configuration.",
|
|
"browse.goLibrary": "Go to library",
|
|
|
|
"mounts.title": "Mounts",
|
|
"mounts.add": "Add WebDAV",
|
|
"mounts.test": "Test connection",
|
|
"mounts.openlistHint": "For OpenList, use its WebDAV URL (e.g. https://host/dav)",
|
|
"mounts.credentialHint": "Credentials are stored on the server only.",
|
|
"mounts.namePlaceholder": "Name, e.g. OpenList",
|
|
"mounts.baseUrlPlaceholder": "https://example.com/dav",
|
|
"mounts.usernamePlaceholder": "Username (optional)",
|
|
"mounts.passwordPlaceholder": "Password / token",
|
|
"mounts.rootPathPlaceholder": "Root path /",
|
|
"mounts.save": "Save",
|
|
"mounts.saveEdit": "Save changes",
|
|
"mounts.saved": "Mount saved",
|
|
"mounts.updated": "Mount updated",
|
|
"mounts.saveError": "Save failed — try again",
|
|
"mounts.delete": "Delete",
|
|
"mounts.deleted": "Mount deleted",
|
|
"mounts.deleteError": "Delete failed",
|
|
"mounts.edit": "Edit",
|
|
"mounts.editing": "Editing",
|
|
"mounts.editHeading": "Edit mount",
|
|
"mounts.cancelEdit": "Cancel edit",
|
|
"mounts.passwordKeepHint": "Password / token (leave blank to keep)",
|
|
"mounts.fieldName": "Name",
|
|
"mounts.fieldUrl": "WebDAV URL",
|
|
"mounts.fieldUser": "Username",
|
|
"mounts.fieldPass": "Password / token",
|
|
"mounts.fieldRoot": "Root path",
|
|
"mounts.listTitle": "Mounted",
|
|
"mounts.disabled": "Disabled",
|
|
"mounts.empty": "No mounts yet",
|
|
"mounts.browseThis": "Browse files",
|
|
"mounts.afterCreateHint": "Mount saved — browse its videos now.",
|
|
"mounts.toBrowse": "Browse",
|
|
"mounts.errNameRequired": "Enter a mount name",
|
|
"mounts.errUrlInvalid": "Enter a valid http(s) URL, e.g. https://host/dav",
|
|
"mounts.confirmDelete": "Confirm delete?",
|
|
"mounts.deleteHint": "Media items scanned from this mount will also be removed",
|
|
"mounts.deletedN": "Mount deleted ({n} media items removed)",
|
|
|
|
"watch.title": "Watch",
|
|
"watch.loading": "Loading…",
|
|
"watch.danmakuOn": "Danmaku on",
|
|
"watch.danmakuOff": "Danmaku off",
|
|
"watch.importXml": "Import local XML",
|
|
"watch.noDanmaku": "No online danmaku yet",
|
|
"watch.noDanmakuImport": "No online danmaku yet; import an XML",
|
|
"watch.danmakuCache": "Danmaku (cache)",
|
|
"watch.danmakuOpenNetwork": "Danmaku (open network)",
|
|
"watch.danmakuLoading": "Loading danmaku…",
|
|
"watch.danmakuCount": "{count} danmaku",
|
|
"watch.backToLibrary": "Back to library",
|
|
"watch.progressHint": "Progress saved every ~5s",
|
|
"watch.loadError": "Failed to load",
|
|
"watch.loadErrorHint": "Could not load this media. Check the link or try again later.",
|
|
"watch.notFound": "Media not found",
|
|
"watch.notFoundHint": "This media may have been removed, or you do not have access.",
|
|
"watch.episodes": "Episodes",
|
|
"watch.prevEpisode": "Prev",
|
|
"watch.nextEpisode": "Next",
|
|
"watch.currentEpisode": "Now playing",
|
|
"watch.playNextCta": "Play next",
|
|
"watch.backToSeries": "Back to series",
|
|
"watch.danmakuSettings": "Danmaku settings",
|
|
"watch.episodeN": "EP {n}",
|
|
"watch.importOk": "Local danmaku imported",
|
|
"watch.importFail": "Danmaku import failed",
|
|
"watch.playFailed": "Playback source failed",
|
|
"watch.playFailedHint":
|
|
"Cannot read the video stream — the mount may be removed or the source unavailable.",
|
|
"watch.replay": "Replay",
|
|
"watch.playbackFinished": "Episode finished",
|
|
"watch.danmakuDisabledHint":
|
|
"Danmaku is off — enable it via the button above or in danmaku settings",
|
|
"watch.danmakuLocal": "Danmaku (local)",
|
|
"watch.rematch": "Rematch",
|
|
"watch.matchCandidates": "Pick episode",
|
|
"watch.matchAutoBound": "High-confidence candidate",
|
|
"watch.matchManual": "Manual match",
|
|
"watch.importSaved": "Danmaku saved",
|
|
"watch.foundSameName": "Same-name candidate",
|
|
"watch.filterTitle": "Danmaku filters",
|
|
"watch.blockKeywordAdd": "Add block word",
|
|
"watch.matchFailed": "Danmaku match failed",
|
|
"watch.matchEmpty": "No episode candidates found",
|
|
"watch.blockKeywordPlaceholder": "Type a block word and press Enter",
|
|
|
|
"danmaku.title": "Danmaku settings",
|
|
"danmaku.opacity": "Opacity",
|
|
"danmaku.density": "Density",
|
|
"danmaku.enabled": "Enable danmaku",
|
|
"danmaku.opacityValue": "Opacity {value}%",
|
|
"danmaku.densityValue": "Density {value}%",
|
|
"danmaku.save": "Save",
|
|
"danmaku.saved": "Saved",
|
|
"danmaku.saveFailed": "Save failed",
|
|
"danmaku.localImportHint":
|
|
"Import local XML on the watch page; this saves server defaults, display options, and block rules.",
|
|
"danmaku.blockKeywords": "Blocked words",
|
|
"danmaku.blockTypes": "Block types",
|
|
"danmaku.blockTypeScroll": "Scroll",
|
|
"danmaku.blockTypeTop": "Top",
|
|
"danmaku.blockTypeBottom": "Bottom",
|
|
"danmaku.openNetworkNotConfigured":
|
|
"Open danmaku network credentials are not configured; online danmaku may be empty.",
|
|
"danmaku.serverPrefsHint":
|
|
"Preferences are stored on this account; watch-page filters and display follow this config.",
|
|
|
|
"auth.login.title": "Sign in",
|
|
"auth.login.heading": "Sign in",
|
|
"auth.login.subtitle": "Continue with username and password",
|
|
"auth.login.submit": "Sign in",
|
|
"auth.login.noAccount": "No account yet?",
|
|
"auth.login.toRegister": "Register",
|
|
"auth.register.title": "Register",
|
|
"auth.register.heading": "Create account",
|
|
"auth.register.subtitle": "Register to access protected pages",
|
|
"auth.register.submit": "Register",
|
|
"auth.register.hasAccount": "Already have an account?",
|
|
"auth.register.toLogin": "Sign in",
|
|
"auth.forgot.title": "Reset password",
|
|
"auth.forgot.heading": "Reset password",
|
|
"auth.forgot.subtitle": "Enter your email to get a reset token",
|
|
"auth.forgot.submit": "Request reset",
|
|
"auth.forgot.toLogin": "Back to sign in",
|
|
"auth.forgot.tokenHint": "Dev token (wire email in production):",
|
|
"auth.forgot.toReset": "Reset with this token",
|
|
"auth.reset.title": "Set new password",
|
|
"auth.reset.heading": "Set new password",
|
|
"auth.reset.subtitle": "Paste the token and enter a new password",
|
|
"auth.reset.submit": "Reset password",
|
|
"auth.settings.title": "Account settings",
|
|
"auth.settings.heading": "Account settings",
|
|
"auth.settings.label": "Account",
|
|
"auth.settings.changeHeading": "Change password",
|
|
"auth.settings.changeSubmit": "Update password",
|
|
"auth.settings.hint": "Other sessions will be signed out after a password change.",
|
|
"auth.field.username": "Username",
|
|
"auth.field.name": "Name",
|
|
"auth.field.email": "Email (optional, for password reset)",
|
|
"auth.field.password": "Password",
|
|
"auth.field.currentPassword": "Current password",
|
|
"auth.field.newPassword": "New password",
|
|
"auth.field.token": "Reset token",
|
|
"auth.menu.login": "Sign in",
|
|
"auth.menu.register": "Register",
|
|
"auth.menu.dashboard": "Dashboard",
|
|
"auth.menu.settings": "Settings",
|
|
"auth.menu.logout": "Sign out",
|
|
"auth.msg.loginOk": "Signed in",
|
|
"auth.msg.registerOk": "Registered",
|
|
"auth.msg.logoutOk": "Signed out",
|
|
"auth.msg.passwordOk": "Password updated",
|
|
"auth.msg.resetOk": "Password reset — please sign in",
|
|
"auth.msg.resetRequested":
|
|
"If the email exists, the reset request was accepted; a reset link will be sent to the mailbox (if no mail service is configured, contact the admin or check server logs)",
|
|
"auth.passwordHint": "At least 8 characters",
|
|
"auth.forgot.emailLabel": "Registered email",
|
|
"auth.err.generic": "Something went wrong — try again",
|
|
|
|
"locale.switch": "Language",
|
|
"locale.zh-CN": "中文",
|
|
"locale.en": "English",
|
|
|
|
"error.home": "Back home",
|
|
"error.retry": "Retry",
|
|
"error.forbidden.label": "Forbidden",
|
|
"error.forbidden.title": "No access",
|
|
"error.forbidden.desc":
|
|
"This account cannot view the resource. Contact an admin or go home if this looks wrong.",
|
|
"error.notFound.label": "Not found",
|
|
"error.notFound.title": "Page not found",
|
|
"error.notFound.desc": "This path is not a known route. It may have moved or mistyped.",
|
|
"error.internal.label": "Error",
|
|
"error.internal.title": "Something broke",
|
|
"error.internal.desc":
|
|
"The server hit an unexpected error. Retry later and report the path if it persists.",
|
|
};
|