Files
Piik/src/client/locales/en.ts
T
AAA 01821ae7ac fix(connection): retry startup and clarify sharing status
Public invitations could stop after an early tunnel-process exit, and connection progress and Viewer status did not consistently describe the current attempt or Host state. This maintenance release adds bounded startup retries, aligns enhanced NAT retry feedback across entry paths, and fixes camera-preview and room-entry layout.

The existing route owner and published contracts remain in place. Four-attempt progress is advertised and opted into; older readers retain supported messages. Interaction/chat experiments are outside this PR.

Validation: npm run check (74 files, 1072 tests and production build), npm run check:go (including Windows capture compile), documentation/repository hygiene, 10 production Viewer-entry discovery cases, and a real public-link gate with remote HTTP/WebSocket access and shutdown cleanup all passed. Existing device/network evidence limits remain documented.

## Release notes

### 中文

这是一次维护更新,重点改善公网邀请启动、连接重试反馈和分享状态提示,并修正两处界面布局问题。

- 公网邀请服务在连接就绪前意外退出时,会自动重试,最多重试 3 次;所有尝试仍共用 30 秒启动预算。
- 增强穿透连接在首次尝试失败后最多重试 3 次,重试进度显示为 1/3、2/3、3/3。邀请链接和房间号加入都支持新版进度提示。
- 修正观众状态提示:房主离线时不再误报“没有可用的媒体线路”,服务器连接恢复与媒体恢复也会分别呈现。
- 修正电视内房间号输入界面的居中位置,以及摄像头选择预览被裁切的问题。
- 尚未确认来源声音状态时,不再提前提示“未分享来源声音”。

本次保持现有版本兼容;新版重试进度需要页面与服务端均支持。额外重试不能保证所有网络都能直连。

### English

This maintenance update improves public invitation startup, connection retry feedback, and sharing status messages, with two layout fixes.

- If the public invitation service exits before connecting, Piik retries up to three times within the existing shared 30-second startup budget.
- Enhanced NAT traversal now allows up to three retries after the initial attempt, shown as 1/3, 2/3, and 3/3. Both invitation links and room-code entry support the updated progress display.
- Viewer status now distinguishes an offline host from an unavailable media route, and reports server reconnection separately from media recovery.
- Fixed the alignment of room-code entry inside the TV and cropping in camera-selection previews.
- The missing-source-audio notice now waits until the source's audio availability is known.

Existing version compatibility is preserved. Updated retry progress requires support from both the page and server. Additional retries cannot guarantee a direct connection on every network.
2026-09-23 22:27:56 +08:00

636 lines
30 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { CopyKey, PlayfulCatalog, TitleFrameCatalog } from "./zh";
// Community translations are data-only and type-checked against zh.
export const en: Record<CopyKey, string> = {
"common.loading": "Loading",
"playback.controls": "Local playback controls",
"playback.play": "Play",
"playback.pause": "Pause on this device",
"playback.mute": "Mute",
"playback.unmute": "Unmute",
"playback.noAudio": "No audio in this share",
"playback.volume": "Adjust volume, from 0% to 200%",
"playback.volumeBasic": "Adjust volume, from 0% to 100%",
"playback.fullscreen": "Fullscreen",
"playback.fullscreenWaiting": "Fullscreen is available when the video is ready.",
"playback.fullscreenFailed": "Couldn’t switch fullscreen mode. Try again.",
"playback.exitFullscreen": "Exit fullscreen",
"playback.pip": "Picture in picture",
"playback.exitPip": "Return to the page",
"playback.pipUnavailable": "Picture in picture is unavailable in this browser",
"playback.pipFailed": "Could not switch picture in picture; try again",
"playback.pipWaiting": "Picture in picture is available once the video is ready",
"debug.start": "Debug",
"debug.startHint": "Reload this page and record browser diagnostics",
"debug.startConfirm": "Start browser diagnostics and reload this page? Any active sharing or viewing will be interrupted.",
"debug.export": "Web report",
"debug.exportHint": "Download browser diagnostics; may include network addresses and device details",
"debug.exportFailed": "Could not download diagnostics; try again",
"brand.home": "Piik home",
"common.roomCode": "Room code",
"common.copy": "Copy",
"common.copied": "Copied",
"common.copyFailed": "Copy failed",
"common.cancel": "Cancel",
"common.retry": "Retry",
"common.refresh": "Refresh page",
"common.edit": "Edit",
"common.close": "Close",
"common.enable": "Enable {name}",
"common.disable": "Disable {name}",
"host.join.hide": "Hide room entry",
"host.advanced.hide": "Hide sharing settings",
"host.password.settingsHide": "Hide password settings",
"host.sourcePicker.audioOn": "Share sound",
"host.sourcePicker.audioOff": "Stop sharing sound",
"host.sourcePicker.audioLocked": "Sound sharing stays unchanged when switching sources",
"common.viewers": "Viewers",
"common.you": "you",
"common.host": "Host",
"common.name.viewerDefault": "Viewer",
"common.name.hostDefault": "Host",
"client.launch.title": "Open Piik",
"client.launch.local": "Local room",
"client.launch.localHint": "Use on the same local network",
"client.launch.link": "Public invite",
"client.launch.linkHint": "Create one temporary public invitation link",
"client.launch.site": "Connect to Site",
"client.launch.siteHint": "Use an existing Piik Site",
"client.launch.siteAddress": "Piik Site address",
"client.launch.lanAddress": "Local invitation address",
"client.launch.lanHint": "Choose an address on the same network as your friends.",
"client.launch.lanUnavailable": "No local network address is available. Connect to a network and refresh this page.",
"client.launch.localAccess": "Site passphrase (optional)",
"client.launch.accessSettings": "Site passphrase",
"client.launch.accessSet": "Set",
"client.launch.localAccessHint": "If set, required to create rooms or join by code. Invite links grant access to their room.",
"client.launch.go": "Open Piik",
"client.launch.starting": "Preparing Piik",
"client.launch.error": "Piik could not start",
"client.launch.reopen": "Check the reason above and the terminal for details, then close and reopen Piik to try again. Enable Debug launch first if you need to record logs.",
"client.launch.debug": "Debug launch",
"client.launch.debugHint": "Record App and browser diagnostics for this launch. Press D in the terminal to export App logs.",
"client.launch.loadFailed": "Could not read the launcher settings; retry",
"client.update.available": "A newer Piik release is available",
"client.update.differentBuild": "This build differs from the official release",
"client.update.official": "An official Piik release is available",
"gate.title": "Site access",
"gate.hint": "Enter the site passphrase",
"gate.password": "Site passphrase",
"gate.submit": "Enter site",
"gate.checking": "Checking site access",
"gate.wrong": "Incorrect passphrase, try again",
"gate.unavailable": "Site access cannot be verified right now",
"gate.malformed": "Invalid room code format",
"gate.malformedHint": "Enter a four-digit room code between 1000 and 9999",
"gate.unavailableRoute": "Unavailable",
"join.title": "Join a room",
"join.hint": "Enter a room code for {site}",
"join.field": "Room code",
"join.submit": "Join",
"join.invalid": "Enter a four-digit room code between 1000 and 9999",
"join.password": "Room password",
"join.passwordError": "Could not join; try again",
"join.passwordRule": "Enter 1-{max} visible characters",
"join.passwordAction": "Enter room password",
"join.full": "Room is full",
"host.title": "{name}'s screen",
"host.start": "Start sharing",
"host.sourcePicker.title": "Choose what to share",
"host.sourcePicker.browser": "Browser picker",
"host.sourcePicker.browserHint": "Choose a source in your browser's capture picker",
"host.sourcePicker.camera": "Share camera: {title}",
"host.camera.denied": "Camera access was not allowed. Allow it in your browser and try again.",
"host.camera.unavailable": "The camera is unavailable. Check that it is connected and not in use by another app.",
"host.microphone.enable": "Turn on microphone",
"host.microphone.label": "Microphone",
"host.microphone.settings": "Microphone settings",
"host.device.microphone": "Microphone",
"host.device.camera": "Camera",
"host.device.default": "Default device",
"host.device.missing": "Selected device unavailable",
"host.device.refresh": "Refresh devices",
"host.device.listFailed": "Could not load devices. Try refreshing.",
"host.device.permission.microphone": "Turn on the microphone and allow access to see the available devices here.",
"host.microphone.volume": "Microphone volume",
"host.microphone.mute": "Mute microphone",
"host.microphone.pending": "Opening microphone",
"host.microphone.browserOnly": "This capture method does not support a microphone yet. Choose Browser or Camera.",
"host.microphone.paused": "Audio and video sharing is paused. Resume to toggle the microphone.",
"host.microphone.busy": "Sharing settings are being updated. Please wait.",
"host.microphone.zeroVolume": "Microphone enabled at 0% volume. Select to mute.",
"host.shareControls": "Sharing controls",
"host.microphone.denied": "Microphone access was not allowed. Allow Piik to use it and try again.",
"host.microphone.unavailable": "The microphone is unavailable. Check its connection and microphone permissions.",
"host.sourcePicker.tab.camera": "Camera",
"host.sourcePicker.tab.browser": "Browser",
"host.sourcePicker.tab.window": "Apps / Windows",
"host.sourcePicker.tab.display": "Screens",
"host.sourcePicker.windowAudio": "Application sound",
"host.sourcePicker.systemAudio": "System sound",
"host.sourcePicker.showCaptureBorder": "Show capture border",
"host.sourcePicker.showCaptureBorderHint": "Turn off to hide the border when Windows allows it. Other captures may keep it visible.",
"host.sourcePicker.empty": "No sources available",
"host.sourcePicker.unavailable": "Cannot connect to Piik App. Keep the App running, open this site from the App, then refresh the list. Allow local-network access if asked.",
"host.sourcePicker.unsupported": "Piik App capture is currently unavailable. Check the encoding setting or the App diagnostics.",
"host.sourcePicker.failed": "Could not read the App's screen and window list. Refresh to try again; if it keeps failing, collect a Debug report.",
"native.incompatible": "Piik App and this page are incompatible. Update Piik App and reload this page, or use Browser.",
"native.fail.edge": "Piik App media connection failed",
"native.fail.signaling": "Piik App signaling failed",
"native.fail.receiver": "Piik App media receive failed",
"host.sourcePicker.window": "Share window: {title}",
"host.sourcePicker.display": "Share screen: {title}",
"host.sourcePicker.system": "System screen or window picker",
"host.sourcePicker.systemAction": "Choose a screen or window with the system picker",
"host.sourcePicker.audioHint": "Application sound for windows; system sound for screens",
"host.sourcePicker.refresh": "Refresh screens and windows",
"host.sourcePicker.loading": "Finding local screens and windows",
"host.join": "Join a room",
"host.starting": "Connecting",
"host.cancelStart": "Cancel",
"host.live": "Sharing",
"host.paused": "Paused",
"host.ended": "Sharing stopped",
"host.roomReady": "Room ready",
"host.pause": "Pause sharing",
"host.resume": "Resume sharing",
"host.switchSource": "Switch source",
"host.switching": "Choosing",
"host.stop": "Stop sharing",
"host.noAudio": "Source sound is not being shared",
"host.localPreviewPaused": "Local preview paused; sharing continues",
"host.pauseNotice": "Audio and video sharing paused",
"host.stopNotice": "Sharing stopped",
"host.roomReplaced": "Room code replaced",
"host.roomInvalid": "Room no longer available; press again to create a new one",
"host.roomReplace": "Replace room code",
"host.roomReplaceConfirm": "Replace the room code: this ends the current share and disconnects viewers",
"host.terminated.stale": "Page updated; refresh to continue",
"host.terminated.session": "This page's session was taken over by another tab",
"host.terminated.signal": "Server connection terminated; refresh to continue",
"host.notice.qualityApplied": "Share settings applied",
"host.notice.qualitySet": "Quality switched to {label}",
"host.notice.audioSet": "Audio quality switched to {label}",
"host.notice.partialApply": "Settings updated, but some viewer connections could not apply them",
"host.notice.sourceSwitch.ok": "Share source switched",
"host.notice.sourceSwitch.partial": "Source switched, but {warning}",
"host.name": "Display name",
"host.nameSave": "Save display name",
"host.nameCancel": "Cancel editing",
"host.nameEdit": "Edit display name",
"host.nameError": "Invalid name, or longer than 24 characters",
"host.nameOffline": "Name saved; others will see it when the connection recovers",
"host.invite": "Invite link",
"host.invite.copy": "Copy invite link",
"host.invite.copyFailed": "Could not copy the link; try again",
"host.invite.rotate": "Rotate invite link; viewers using the old link will need to rejoin",
"host.invite.revoke": "Revoke invite link and disconnect viewers who joined with it",
"host.invite.rotateShort": "Rotate link",
"host.invite.revokeShort": "Revoke link",
"host.invite.updated": "Invite link rotated",
"host.invite.revoked": "Invite link revoked",
"host.invite.emptyOpen": "No invite link; the room code still works",
"host.invite.address": "Room address",
"host.invite.copyAddress": "Copy room address",
"host.invite.includeCredential": "Skip passwords",
"host.invite.credentialHint": "Anyone with this link can join the room",
"host.invite.credentialRequired": "This room is invite-only; turn on Skip passwords",
"host.invite.emptyPassword": "No invite link; room code plus password still works",
"host.invite.emptyPrivate": "No invite link; rotate one before inviting",
"host.policy": "Admission",
"host.policy.open": "Open",
"host.policy.private": "Private",
"host.policy.currentOpen": "Room code entry",
"host.policy.currentPassword": "Room code + password",
"host.policy.currentInvite": "Invite only",
"host.policy.openHint": "Anyone with the room code can join",
"host.policy.privateHint": "Join with an invite link, or set a room password to allow code entry",
"host.policy.privatePasswordHint": "Join with an invite link, or the room code and password",
"host.policy.setOpen": "Room is now open",
"host.policy.setPrivateInvite": "Room is now private, invite link only",
"host.policy.setPrivatePassword": "Room is now private, invite link or password",
"host.password.set": "Room password set",
"host.password.unset": "No room password",
"host.password.placeholder": "Set one to allow code entry",
"host.password.setAction": "Set room password",
"host.password.changeAction": "Change room password",
"host.password.remove": "Remove room password",
"host.password.show": "Show room password",
"host.password.hide": "Hide room password",
"host.password.saved": "Room password set",
"host.password.updated": "Room password updated",
"host.password.removed": "Password removed; invite link only",
"host.password.rule": "1-{max} visible characters",
"host.accessFailed": "Cannot update room settings right now; try again later",
"host.viewers.empty": "No viewers yet",
"host.viewerOverview": "All viewer connections",
"host.quality": "Video preset",
"host.quality.720p30": "720p · 30 fps",
"host.quality.1080p30": "1080p · 30 fps",
"host.quality.1080p60": "1080p · 60 fps",
"host.advanced": "Sharing settings",
"host.settings.button": "Settings",
"host.settings.picture": "Picture",
"host.settings.sound": "Sound",
"host.settings.technical": "Connection & encoding",
"host.advanced.resolution": "Resolution ceiling",
"host.advanced.framerate": "Frame rate ceiling",
"host.advanced.bitrate": "Video bitrate ceiling",
"host.advanced.preference": "Picture preference",
"host.advanced.preference.resolution": "Clarity",
"host.advanced.preference.balanced": "Balanced",
"host.advanced.preference.framerate": "Fluency",
"host.advanced.preference.resolutionHint": "Keep detail",
"host.advanced.preference.balancedHint": "Weigh automatically",
"host.advanced.preference.framerateHint": "Prefer frame rate",
"host.advanced.audio": "Audio quality",
"host.advanced.audio.saver": "Standard",
"host.advanced.audio.music": "Music",
"host.advanced.audio.veryHigh": "Studio",
"host.advanced.route": "Route policy",
"host.advanced.route.topo": "Auto topology tuning",
"host.advanced.route.topoHint": "Gradually pick healthier paths while live",
"host.advanced.route.peerOnly": "Privacy mode",
"host.advanced.route.peerOnlyHint":
"Share over peer-to-peer (P2P) connections only. Viewers cannot watch if those connections fail",
"host.advanced.route.peerOnlyRequired":
"This service supports peer-to-peer (P2P) media only, so Privacy mode is always on",
"host.advanced.route.natPrediction": "Enhanced NAT traversal",
"host.advanced.route.natPredictionHint":
"Use extra probes and retries to help connect across some restricted networks. Available in browsers and the App",
"host.advanced.route.natPredictionUnavailable":
"This service has not enabled the enhancement. Standard P2P connections are still attempted",
"host.advanced.codec": "Video codec",
"host.advanced.codec.auto": "Auto",
"host.advanced.codec.autoHint": "Pick automatically",
"host.advanced.codec.vp8Hint": "Compatibility first",
"host.advanced.codec.h264Hint": "Hardware first",
"host.details": "Show connection details",
"host.details.hide": "Hide connection details",
"host.topology": "Connection topology",
"host.topology.show": "Show connection topology",
"host.topology.hide": "Hide connection topology",
"host.topology.pending": "Connecting",
"state.signal.connected": "Server connected",
"state.signal.connecting": "Connecting",
"state.signal.reconnecting": "Recovering",
"state.signal.offline": "Not connected to server",
"state.peer.connected": "Connected",
"state.peer.connecting": "Connecting",
"state.peer.routing": "Routing",
"state.peer.waiting": "Waiting for share",
"state.peer.reconnecting": "Recovering",
"state.peer.disconnected": "Connection lost",
"state.route.p2p": "P2P direct",
"state.route.sfu": "Via server",
"stats.title": "Connection data",
"stats.relay": "Relay data",
"stats.downstream": "Downstream receive data",
"stats.more": "Detailed metrics",
"stats.less": "Hide detailed metrics",
"stats.resolution": "Resolution",
"stats.fps": "Frame rate",
"stats.bitrate": "Bitrate",
"stats.loss": "Packet loss",
"stats.rtt": "RTT",
"stats.codec": "Video codec",
"stats.jitter": "Jitter",
"stats.dropped": "Dropped frames (interval)",
"stats.encoder": "Encoder",
"stats.audio": "Audio bitrate",
"stats.unknown": "Unknown",
"stats.outgoing": "Available uplink",
"stats.qualityState": "Quality state",
"stats.quality.normal": "Normal",
"stats.quality.bandwidth": "Bandwidth-limited",
"stats.quality.cpu": "Encoder-limited",
"stats.quality.other": "Other limit",
"stats.quality.unclassified": "Unclassified limit",
"stats.capture": "Capture settings",
"stats.inputFps": "Encoder input rate",
"stats.encodeMs": "Encode time/frame",
"stats.decodeMs": "Decode time/frame",
"state.peer.stuttering": "Recent playback stutter",
"stats.freezeCount": "Freezes",
"stats.freezeDuration": "Freeze duration",
"stats.audioLoss": "Audio packet loss",
"stats.audioJitter": "Audio jitter",
"stats.playoutDelta": "A/V playout delta",
"stats.videoBuffer": "Video buffer",
"stats.audioBuffer": "Audio buffer",
"stats.audioConcealedRate": "Audio concealment rate",
"stats.audioConcealed": "Audio concealments",
"viewer.title": "{name}'s screen",
"viewer.titleFallback": "A friend's screen",
"viewer.title.watching": "Watching",
"viewer.title.waiting": "Waiting",
"viewer.reconnect": "Reconnect",
"viewer.theater": "Theater mode",
"viewer.theater.exit": "Exit theater mode",
"viewer.msg.joining": "Joining the room",
"viewer.msg.waitingHost": "Waiting for the host to share",
"viewer.msg.preparingP2p": "Establishing P2P",
"viewer.msg.connectionRetry": "Retrying ({current}/{total})",
"viewer.msg.preparingSfu": "Connecting backup route",
"viewer.msg.allocating": "Allocating a route",
"viewer.msg.receiving": "Receiving video",
"viewer.msg.playing": "Playing",
"viewer.msg.needsPlay": "Tap to play",
"viewer.msg.hostPaused": "Host paused",
"viewer.msg.recovering": "Recovering connection",
"viewer.msg.routeFailed": "No media route available",
"viewer.msg.playbackFailed": "The browser cannot play this stream",
"viewer.msg.hostOffline": "Host connection lost",
"viewer.msg.serverError": "Connection service unavailable",
"viewer.msg.notFound": "Room not found",
"viewer.msg.denied": "This room cannot be joined by code right now",
"viewer.msg.invalidInvite": "Invite link is no longer valid",
"viewer.msg.closed": "Room closed",
"viewer.msg.full": "Room is full",
"viewer.msg.accessFailed": "Unable to join the room",
"viewer.hint.accessFailed": "Reload this page to try again, or use an invite link from the host.",
"viewer.hint.full": "Try again when a seat opens up.",
"viewer.msg.stale": "Page updated; refresh to continue",
"viewer.msg.sessionReplaced": "This page's session was taken over by another tab",
"viewer.msg.signalTerminated": "Connection terminated; refresh to continue",
"viewer.notice.signalRecovering": "Server reconnecting",
"viewer.notice.hostOffline": "Host connection lost; picture may freeze",
"viewer.hint.denied": "Use the host's invite link, or try the room password.",
"viewer.hint.notFound": "Check that the room code belongs to {site}, or open the host’s invite link.",
"viewer.hint.invite": "Ask the host for a new invite link.",
"viewer.hint.generic": "Check your entry point and try again.",
"mode.language": "Interface language",
"mode.more": "More languages",
"mode.vis": "Visual only",
"theme.dark": "Switch to dark mode",
"theme.light": "Switch to light mode",
"theme.dark.short": "Dark",
"theme.light.short": "Light",
"gate.connectFailed": "Cannot reach the site access service; try again",
"gate.expired": "Site access expired; verify again",
"gate.serviceUnavailable": "Site access service unavailable ({status})",
"host.notStarted": "Not started",
"host.startCancelled": "Start cancelled",
"host.switchingSource": "Switching source",
"host.capture.fpsUnknown": "Unknown frame rate",
"host.capture.codecPending": "Codec pending",
"host.capture.hasAudio": "Source audio included",
"host.capture.noAudio": "No source audio",
"host.stageAria": "Share or join a room",
"host.captureAria": "Actual capture parameters",
"host.quality.title": "{label} · {mbps} Mbps",
"host.audio.title": "{label} · {kbps} kbps ceiling",
"host.roomClosed": "Room closed",
"host.pause.noTracksResume": "No resumable media tracks in the current share",
"host.pause.signalRecovering": "Server reconnecting; sharing stays paused",
"host.pause.noTracksPause": "No pausable media tracks in the current share",
"host.pause.stillPaused": "Sharing stays paused",
"host.shareEnded": "The current share has ended",
"host.fail.start": "Failed to start sharing",
"host.err.createConnection": "Failed to create the connection",
"host.err.codecUnsupported": "This browser cannot use a supported video codec",
"host.fail.source": "Failed to switch share source",
"host.fail.quality": "Failed to apply quality settings",
"host.fail.connection": "Viewer connection handling failed",
"host.fail.room": "Room operation failed",
"host.err.invalidToken": "Share credentials are no longer valid; create the room again",
"host.err.accessDenied": "Not permitted",
"host.err.alreadyConnected": "This room is already shared in another page",
"host.err.peerGone": "The viewer connection has left",
"host.err.forbidden": "Action unavailable",
"host.err.serverError": "Service temporarily unavailable; try again later",
"host.err.createRoomStatus": "Cannot create a room right now ({status})",
"host.err.replaceRoomStatus": "Cannot change rooms right now ({status})",
"host.err.updateRoomStatus": "Cannot update room settings right now ({status})",
"host.capture.cancelled": "Screen selection cancelled or not permitted",
"host.capture.noSource": "No shareable screen source available",
"host.capture.readFailed": "The browser cannot read the selected source right now",
"host.capture.unavailable": "This page cannot start screen sharing",
"host.notice.reconnecting": "some viewers are reconnecting",
"host.notice.sfuRecovering": "Server forwarding source switch failed; restoring viewer connections",
"host.password.inputPlaceholder": "Enter password",
"stats.warn.codec": "Video codec is {codec}; expected H264 or VP8",
"stats.warn.audioCodec": "Audio codec is {codec}; expected Opus",
"viewer.msg.joinUnavailable": "Cannot join the room right now",
"viewer.stageAria": "Shared picture",
"host.warn.audioUnread": "The browser did not report the audio bitrate ceiling",
"host.warn.audioRewritten": "The browser rewrote the audio bitrate ceiling to {kbps} kbps",
"host.warn.senderPartial": "The browser did not fully accept {params}",
"host.warn.param.maxBitrate": "bitrate ceiling",
"host.warn.param.maxFramerate": "frame rate ceiling",
"host.warn.param.scaleResolutionDownBy": "resolution scaling",
"host.warn.param.degradationPreference": "quality preference",
"host.warn.param.scalabilityMode": "scalability mode",
"host.warn.sfuRecover": "Server forwarding: {params} failed; automatic recovery started",
"host.warn.sfuStage.connect": "connect",
"host.warn.sfuStage.source": "source",
"host.warn.sfuStage.videoPublish": "video publish",
"host.warn.sfuStage.senderConfig": "sender config",
"host.warn.sfuStage.audioPublish": "audio publish",
"host.warn.sfuStage.transport": "transport",
"host.fail.sfuSwitch": "Failed to switch the server forwarding source",
"host.fail.sfuAudioParams": "Failed to apply audio parameters for server forwarding",
"host.fail.sfuParams": "Failed to apply sender parameters for server forwarding",
};
export const enTitleFrames = {
hostActive: { label: en["host.live"], variations: [
"Screen's up",
"Sending pixels",
"Picture in motion",
"Tiny TV at work",
"Show time",
"The pixels must flow",
"Pixels go brrr",
"Let it cook",
"The game's afoot",
"Curtain up",
"Tiny screen, big moment",
"Lights, camera, screen",
"Side quest: sharing",
"Show and tell time",
"Keep calm and share on",
"Pixels, assemble",
"Screening in progress",
"Sharing the plot",
"Behold, my screen",
] },
viewerActive: { label: en["viewer.title.watching"], variations: [
"Tuned in",
"Pixels incoming",
"Eyes on screen",
"Popcorn ready",
"Tiny TV rolling",
"Front-row seat",
"Let them cook",
"Audience mode: engaged",
"Popcorn mode on",
"A wild screen appears",
"Front row secured",
"One does not simply scroll past",
"Came, saw, stayed",
"This is the good bit",
"Here for the plot",
"Achievement: spectator",
"Just one more scene",
"The plot thickens",
"Show me the pixels",
] },
paused: { label: en["host.paused"], variations: [] },
hostStarting: { label: en["host.starting"], variations: [
"Setting the stage",
"Getting the pixels together",
"Preparing the opening scene",
"Getting ready for show and tell",
"One moment before act one",
"Side quest: set the stage",
"The tiny TV is warming up",
"Preparing a pixel premiere",
"Lights, camera... getting ready",
"Arranging the opening credits",
"Preparing the main attraction",
"Pixels reporting for duty",
"Getting the screen ready",
"Setting up this episode",
"Preparing the big reveal",
"Rolling out the tiny carpet",
"Getting ready to roll",
] },
hostEnded: { label: en["host.ended"], variations: [] },
hostReady: { label: en["host.roomReady"], variations: [
"Couch is ready",
"Ready when you are",
"Tiny TV standing by",
"Remote at the ready",
"Lights are set",
"Your channel awaits",
"Tea is ready",
"A seat awaits your quest",
"Bring a screen, bring a story",
"All set for show and tell",
"Your screen, should you accept",
"The stage is yours",
"Main quest: pick a screen",
"A little room for adventure",
"Ready for the director's cut",
"Your very own premiere",
"Screen slot available",
"The couch has entered the lobby",
"All the room's a stage",
] },
hostIdle: { label: en["host.notStarted"], variations: [
"Tiny TV is resting",
"Waiting for a screen",
"Antenna taking a nap",
"Screen is dreaming",
"Remote is daydreaming",
"Waiting for showtime",
"Quiet little room",
"Tiny TV is AFK",
"Side quest: doing nothing",
"The plot is still off-screen",
"Taking five",
"Peace, quiet, pixels later",
"The screen can wait",
"An intermission before act one",
"Currently between adventures",
"Saving energy for the premiere",
"Daydreaming in widescreen",
"The couch potato life",
"Taking a long rest",
] },
viewerReady: { label: "Press Play", variations: [] },
viewerClosed: { label: en["viewer.msg.closed"], variations: [] },
viewerUnavailable: { label: "Unavailable", variations: [] },
hostUnavailable: { label: en["host.fail.start"], variations: [] },
viewerWaiting: { label: en["viewer.title.waiting"], variations: [
"Waiting for showtime",
"Getting the popcorn",
"Front row reserved",
"Couch saved you a spot",
"Remote in hand",
"Waiting for the picture",
"Audience standing by",
"In the pre-show lobby",
"Waiting for the plot",
"Side quest: bring snacks",
"The pre-show intermission",
"Ready, set... waiting",
"Waiting on a wild screen",
"Camped out for the premiere",
"Pre-show snack break",
"Patience, young viewer",
"Waiting for act one",
"Seat taken, screen pending",
"The suspense is real",
] },
} satisfies TitleFrameCatalog;
// Local cultural references are independent entries, not translation keys.
export const enPlayful: PlayfulCatalog = {
welcome: [
{ text: "Your seat is right here.", symbols: ["couch", "heart"] },
{ text: "Single-player story. Multiplayer audience.", symbols: ["gamepad", "users"] },
{ text: "Three, two, one… roll it.", symbols: ["clock", "clapperboard"] },
{ text: "Side quest: share a little joy.", symbols: ["branch", "heart"] },
{ text: "Snacks optional. Good company recommended.", symbols: ["popcorn", "play"] },
{ text: "And now, your scene.", symbols: ["clapperboard", "arrowRight"] },
{ text: "Save your game. Stay a while.", symbols: ["save", "moon"] },
{ text: "Unlocked: someone who gets it.", symbols: ["trophy", "heart"] },
{ text: "Big moment ahead. Bring snacks.", symbols: ["zap", "popcorn"] },
{ text: "Stay for the credits. There might be a surprise.", symbols: ["clapperboard", "gift"] },
{ text: "Some moments deserve a second audience.", symbols: ["refresh", "users"] },
{ text: "You lead. I'll bring the folding chair.", symbols: ["arrowRight", "couch"] },
{ text: "No main quest today. Let's wander.", symbols: ["mountain", "branch"] },
{ text: "Every great moment needs a witness.", symbols: ["tv", "eye"] },
{ text: "Made a little something. Come take a look.", symbols: ["bulb", "eye"] },
{ text: "One screen. A very enthusiastic audience.", symbols: ["cast", "users"] },
{ text: "What's on the shot list, director?", symbols: ["clapperboard", "play"] },
{ text: "Next level? Bring your people.", symbols: ["flag", "users"] },
{ text: "Joy travels well.", symbols: ["cast", "heart"] },
{ text: "Twenty seats. Plenty of stories.", symbols: ["couch", "users"] },
],
waiting: [
"Pull up a comfy chair.",
"The snack side quest is optional.",
"Sweet popcorn or salty?",
"A fine moment for a stretch.",
"Hydration check.",
"The couch isn't going anywhere.",
"Side quest: get comfortable.",
"Front-row energy, sofa comfort.",
"Small pixels. Big personalities.",
"Legendary loot: one comfy chair.",
"There's a story off-screen, too.",
"Cats get the sofa. Keyboards get a break.",
"Blink if you can see the tiny TV.",
"Keep calm and pass the popcorn.",
"Remember to loot the snack bowl.",
"Insert dramatic narration here.",
"Passive skill: daydreaming.",
"A little break in the action.",
"Dance break? Seated dancing counts.",
"Checkpoint reached: the couch.",
],
};