visual-game.js
36.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
export default {
appName: 'voko.world',
productBrandSub: 'Your kosmo is calling.',
mobileCredit: { title: 'My Credits', buyCredits: 'Buy Credits', history: 'Credit History', purchaseHistory: 'Purchases', usageHistory: 'Usage', emptyHistory: 'No credit history yet.', loadMore: 'Load more', balanceAfter: 'Balance: {points}', pending: 'Pending', paid: 'Paid', canceled: 'Canceled', failed: 'Failed', refunded: 'Refunded', disputed: 'Disputed', autoReload: 'Auto-Reload', bonusCredits: 'Bonus Credits', buyNow: 'Buy Now', lowBalance: 'Low Balance', enabled: 'Enabled', whenBelow: 'When my balance falls below', choosePack: 'Choose a reload pack', consent: 'I authorize Voko to charge this pack automatically whenever my balance reaches the threshold. Purchases are final except where required by law.', terms: 'Terms of Use', continueSecurely: 'Continue securely', save: 'Save settings', disable: 'Turn off Auto-Reload', setupComplete: 'Payment method saved. Auto-Reload is now enabled.', setupCanceled: 'Auto-Reload setup was canceled.', actionRequired: 'The last reload failed. Set up your payment method again to resume.', howTitle: 'How Auto-Reload works', howBalance: 'We buy the selected pack when your balance reaches or falls below the threshold.', howSecure: 'Payment details are collected and stored securely by Stripe; Voko never stores your card number.', howCancel: 'You can turn Auto-Reload off at any time. Turning it off does not refund previous purchases.' },
nav: { play: 'Play', notes: 'Clues', state: 'State', editor: 'Authoring' },
scenario: { title: 'Scenario', available: 'Scenarios', assetManifest: 'Assets' },
character: { title: 'Character', start: 'Start game', current: 'Current character' },
stage: { empty: 'Choose a scenario and character', turn: 'Turn {turn}', ending: 'Ending', transcript: 'Transcript' },
composer: { act: 'Act', say: 'Say', placeholder: 'Enter an action or line' },
actions: { refresh: 'Refresh', start: 'Start', home: 'Home', backHome: 'Back', newRun: 'Restart', send: 'Send', save: 'Save', compile: 'Compile', publish: 'Publish', rollback: 'Rollback', upload: 'Upload', select: 'Select', endingText: 'Ending text', load: 'Load', backToGame: 'Back', cancel: 'Cancel', confirm: 'Confirm' },
account: {
signIn: 'Sign in',
signedIn: 'Signed in',
signedOut: 'Signed out',
sessionExpired: 'Your session expired. Please sign in again.',
logoutFailed: 'Log out failed. Please try again.',
registered: 'Account created',
email: 'Email',
emailPlaceholder: 'Email address',
password: 'Password',
passwordPlaceholder: 'Enter password',
confirmPassword: 'Confirm password',
confirmPasswordPlaceholder: 'Enter password again',
verificationCode: 'Verification code',
verificationCodePlaceholder: 'Enter the 6-digit email code',
inviteToken: 'Invite code',
inviteTokenPlaceholder: 'Enter the 8-character invite code',
sendCode: 'Send code',
resendCode: 'Resend',
codeSent: 'Code sent. Check your email.',
codeRequired: 'Enter the email verification code first.',
inviteInvalid: 'Enter a valid 8-character invite code.',
emailRequired: 'Enter your email first.',
emailInvalid: 'Enter a valid email address.',
passwordRequired: 'Enter your password first.',
confirmPasswordRequired: 'Confirm your password first.',
loginFieldsRequired: 'Enter your email and password.',
inviteEmailMismatch: 'This email does not match the application email linked to the invite code.',
passwordMismatch: 'Passwords do not match.',
createAccount: 'Create account',
hasAccount: 'Already have an account?',
noAccount: 'No account yet?',
termsHint: 'By continuing, you confirm that this email belongs to you. Your account will save your progress, orders, and credits.',
login: 'Log in',
register: 'Register',
reset: 'Reset',
forgotPassword: 'Forgot password?',
resetPassword: 'Reset password',
resetTitle: 'Reset password',
resetBody: 'Verify your email code, then set a new password. Existing sessions for this account will be revoked.',
resetCodeSent: 'Reset code sent. Check your email.',
resetFieldsRequired: 'Enter your email, verification code, new password, and confirmation password.',
passwordResetDone: 'Password reset. Log in with the new password.',
logout: 'Log out',
logoutAll: 'Log out everywhere',
credits: 'Credits',
buy: 'Buy credits',
billing: 'Billing',
promptTitle: 'Sign in or register to start',
promptBody: 'Your account saves progress, save slots, endings, credits, and orders so you can continue on another device.',
signedInBody: 'Your progress, save slots, and credits are saved to your account.',
registerAndStart: 'Create an account and choose a character',
guestStart: 'Try as guest',
savedHint: 'Sign in to auto-save progress and save slots',
currentAccount: 'Current account',
saveKicker: 'Cloud save',
saveTitle: 'Save your progress and purchases',
loginTitle: 'Log in',
loginBody: 'After you log in, we will merge any guest progress from this device when possible.',
registerTitle: 'Create account',
registerBody: 'Create an account with an invite code, then verify your email. You will be signed in, receive 100 credits, and keep any guest progress from this device.',
openBilling: 'View credits and orders',
mergePending: 'Merging guest progress from this device…',
mergeSuccess: 'Guest data merged: {details}.',
mergeSeparator: ', ',
mergeRuns: '{count} runs',
mergeTurns: '{count} turns',
mergeProgress: '{count} progress records',
mergeSaveSlots: '{count} save slots',
mergeEndingRecords: '{count} endings',
mergeEvents: '{count} events',
mergeAgentCalls: '{count} AI calls',
mergeBillingLedger: '{count} ledger entries',
mergeBillingUnlocks: '{count} paid unlocks',
mergeBillingFreeQuota: '{count} free quotas',
mergePaymentOrders: '{count} orders',
mergeEmpty: 'No local guest progress to merge.',
mergeFailed: 'You are signed in, but we could not merge your guest progress. Refresh the page and try again later.',
},
billing: {
title: 'Credits and orders',
subtitle: 'View your balance, credit purchases, transaction history, and orders.',
guestSubtitle: 'You can browse credit packs as a guest. Sign in to make a purchase or view your balance, orders, and transaction history.',
balance: 'Current balance',
products: 'Credit packs',
ledger: 'Credit ledger',
orders: 'Orders',
refresh: 'Refresh',
testModeTitle: 'Stripe test environment',
testModeBody: 'Checkout uses test payments. No real charge will be made.',
testPurchase: 'Test purchase',
buy: 'Buy {points} credits',
checkoutReady: 'Checkout link is ready.',
openCheckout: 'Open checkout',
selectedPack: 'Selected',
selectedProduct: '{title} selected. Your account balance will increase by {points} credits.',
signInToBuy: 'Sign in to buy',
readyToBuy: 'Ready to buy credits',
readyToBuyBody: 'Choose a credit pack and continue to checkout. Orders may show processing before confirmation.',
checkoutPending: 'Redirecting to checkout…',
checkoutReturnedSuccess: 'Welcome back. We are refreshing your balance and orders. Credits will be added once payment is confirmed.',
checkoutReturnedCancel: 'Checkout was canceled or not completed. No credits were added.',
signInHint: 'Sign in to view balance, orders, and ledger entries.',
emptyProducts: 'No credit packs.',
emptyLedger: 'No ledger entries.',
emptyOrders: 'No orders.',
productFallback: 'Credit pack',
points: '{points} credits',
price: '{amount} {currency}',
ledgerAmount: '{amount} credits',
orderStatus: 'Status {status}',
orderStatusPending: 'Processing',
orderStatusPaid: 'Paid',
orderStatusRefunded: 'Refunded',
orderStatusDisputed: 'Disputed',
orderStatusUnknown: 'Unknown status',
ledgerReasonSignupGift: 'Signup gift',
ledgerReasonCheckout: 'Credit purchase',
ledgerReasonRefund: 'Order refund',
ledgerReasonChargeback: 'Chargeback reversal',
ledgerReasonUnlock: 'Content unlock',
paywallTitle: 'Unlock with credits',
paywallTarget: 'Target',
paywallReason: 'Reason',
paywallGenericReason: 'Credits are required to continue this content.',
paywallFreeContinue: 'You still have a free play available. You can continue without spending credits.',
paywallInsufficient: 'You do not have enough credits. Buy more credits to continue.',
paywallPrice: 'Required credits',
paywallFreeQuota: 'Free quota left',
paywallBalance: 'Available balance',
},
endings: {
title: 'Ending collection',
unlocked: 'Unlocked',
locked: 'Locked',
collected: 'Collected {count}/{total}',
lockedHint: 'Reach this ending to unlock it.',
},
saves: {
title: 'Save slots',
progress: 'Current progress',
activeRun: 'Current run',
slot: 'Slot {slotNo}',
savedRun: 'Saved progress',
emptySlot: 'Empty slot',
emptyHint: 'Save your current progress here so you can return to it later.',
saveHere: 'Save',
overwrite: 'Overwrite save',
restore: 'Load',
delete: 'Delete',
saved: 'Game saved.',
restored: 'Save loaded.',
deleted: 'Save deleted.',
conflict: 'This slot already contains a save. Confirm that you want to overwrite it.',
restoreConfirm: 'Loading this save will replace your current game state.',
deleteConfirm: 'Deleting this save point cannot be undone.',
noRunHint: 'There is no active game to save. Start or continue a game first.',
},
map: {
title: 'Map',
current: 'Current location',
route: 'Available route',
routes: 'Available destinations',
locked: 'Locked',
moveTo: 'Go to {location}',
objects: 'Inspectable objects',
inspect: 'Inspect',
inspected: 'Inspected',
inspectObject: 'Inspect {object}',
},
videoCenter: {
eyebrow: 'Player playthroughs',
title: 'Video Center',
backHome: 'Back',
chooseScenario: 'Choose scenario',
allScenarios: 'All scenarios',
searchScenario: 'Search scenarios',
scenarioPaintedSkin: 'Painted Skin',
scenarioGuian: 'Guian Heart-Loss Case',
nowPlaying: 'Now playing',
player: 'Player',
views: 'Views',
ending: 'Ending',
duration: 'Duration',
libraryEyebrow: 'More from this scenario',
allVideos: 'All videos',
sort: 'Sort videos',
recommended: 'Recommended',
latest: 'Latest',
videoMeta: '{player} · {date}',
emptyTitle: 'No player videos yet',
emptyText: 'No public playthroughs are available for this scenario.',
viewAll: 'View all player videos',
discoverStories: 'See how other players shaped the story',
backPrevious: 'Back', aiSummary: 'AI summary:', endingCount: ' endings', publicSeriesCount: ' public series', totalPlays: ' total plays', seriesList: 'Player series', completed: 'Complete', updating: 'Episode {count} · Ongoing', seriesMetrics: 'Episodes: {episodes} · ▶ {plays} · ♥ {likes}', watchSeries: 'Watch this series', playOwnSeries: 'Create your own story', minutes: '{count} min', share: 'Share', shareCopied: 'Link copied', shareFailed: 'Could not share this video', like: 'Like', liked: 'Liked', save: 'Save', saved: 'Saved', storyHighlight: 'Story highlights', startPlay: 'Play now · Rewrite this ending', episodeList: 'Episodes', episodeTotal: 'Episodes: {count}', episodeTitle: 'Episode {count} · {title}',
},
runHistory: {
eyebrow: 'Your play history',
title: 'My Runs',
allRuns: 'All runs',
count: 'Games: {count}',
empty: 'You have not played any games yet. Start a game and it will appear here.',
loadFailed: 'Could not load your games: {error}',
noTime: 'Unknown time',
statusActive: 'Active',
statusCompleted: 'Ended',
videoNone: 'No video',
videoQueued: 'Video queued',
videoRunning: 'Generating video',
videoReady: 'Video ready',
videoFailed: 'Video generation failed',
videoRetry: 'Generate again',
untitledEnding: 'No ending yet',
meta: '{scenario} · Turn {turn} · {date}',
openRun: 'Continue game',
openNotes: 'Notes',
watchVideo: 'Watch video',
},
mobileProfile: { title: 'MY', settings: 'Settings', content: 'Content', benefits: 'Benefits', creator: 'Creator', support: 'Inquiries and support', contact: 'Customer support', terms: 'Terms of Use', privacy: 'Privacy Policy', social: 'Social media' },
profileEditor: { editProfile: 'Edit profile', nameSection: 'Profile name', nameHint: 'This name is shown with your games and videos.', name: 'Name', saveName: 'Save name', nameRequired: 'Enter your name.', nameSaved: 'Your name has been updated.', passwordSection: 'Change password', passwordHint: 'Verify your account using the code sent to your email.', email: 'Email', verificationCode: 'Email verification code', sendCode: 'Send code', codeSent: 'A verification code was sent to your email.', newPassword: 'New password', confirmPassword: 'Confirm new password', savePassword: 'Update password', passwordRequirements: 'Enter the verification code and a password of at least 8 characters.', passwordMismatch: 'The passwords do not match.', passwordSaved: 'Your password has been updated.' },
myGames: { recent: 'Recently played', popular: 'Popular stories', emptyRecent: 'Your recently played stories will appear here.', loadError: 'Could not load your games. Please try again.' },
storyDetail: { title: 'Story Information', description: 'Story Description', episodes: '{count} Episodes', endings: '{count} Endings', by: 'By {name}', lastUpdate: 'Last Update', newPlay: 'New Play', notFound: 'This story could not be found.', loadError: 'Could not load the story. Please try again.' },
accountCenter: { loadFailed: 'Could not load your profile', loadTimeout: 'The request timed out. Check your connection and try again.', title: 'Account', eyebrow: 'Player profile', runs: 'My games', videos: 'My videos', favorites: 'Saved videos', watchSeason: 'Watch season', emptyGames: 'You have not started any games yet.', emptyVideos: 'You have not generated any videos yet.', emptyFavorites: 'You have not saved any videos yet.', lastPlayed: 'Last played · {date}', currentTurn: 'In progress · Turn {turn}', noActiveRun: 'No game in progress', endingsUnlocked: 'Endings: {unlocked}/{total}', runsPlayed: 'Games played: {count}', continueGame: 'Continue game', startGame: 'Start game', viewHistory: 'View history', levelProgress: 'Levels played: {played}/{total}', viewSeasonDetails: 'View season details', hideSeasonDetails: 'Hide season details', levelInProgress: 'In progress', levelCompleted: 'Complete', endingLabel: 'Ending reached', cluesLabel: 'Clues found', clueCount: 'Clues: {count}', viewRunRecords: 'View game history', runRecordSummary: 'Clues: {clues} · Endings: {ending}', noEnding: 'No ending reached yet', noClues: 'No clues found yet', seasonDetailsLoadFailed: 'Could not load this season. Try again.', public: 'Public', private: 'Only me', deleteVideo: 'Delete video', confirmDelete: 'Delete video?', signInTitle: 'Sign in to continue', signInBody: 'Sign in to manage your games and videos.' },
visual: {
eyebrow: 'TRPG Visual',
chooseScript: 'Choose script',
actTitle: 'Act {count} · {title}',
actLocked: '{title} is locked',
actSealed: 'Act Sealed',
toBeContinued: 'To be continued',
finishPreviousAct: 'Reach an ending in the previous act',
revealAfterAct: 'Revealed after completing Act {count}',
moreScripts: 'More levels',
comingSoon: 'Coming soon',
watchScriptVideo: 'Watch story video',
scenarioKicker: 'Scenario',
hiddenTitle: 'Tianyou',
revealedTitle: 'Painted Skin',
objectives: 'Current objectives',
playHistory: 'Play history',
objectiveLabel: 'Objective',
close: 'Close',
noObjectives: 'No active objectives.',
taskProgress: 'Steps {done}/{total}',
entryKicker: 'Enter game',
entryTitle: 'Enter the room tonight',
emptyScenarioEyebrow: 'Scenario unavailable',
emptyScenarioTitle: 'No playable scenario',
emptyScenarioText: 'The server did not return a published scenario. Refresh or check publishing status.',
emptyScenarioActionTitle: 'Waiting for publication',
emptyScenarioActionText: 'A game cannot start until a published scenario is available.',
continueTitle: 'Continue your last game',
entryBody: 'Choose a character before entering the old room.',
chooseCharacter: 'Choose character',
enterStory: 'Enter the story',
back: 'Back',
skip: 'Skip',
editor: 'Editor',
debugPage: 'Debug',
state: 'State',
settings: 'Settings',
language: 'Language',
languageUnavailable: 'This level does not have an available translation for that language yet.',
turnVideoMatching: 'Smart CG matching',
turnVideoMatchingHint: 'When enabled, similar story beats can be matched to this turn. When off, only exact CG bindings play.',
turnVideoMatchingOn: 'On',
turnVideoMatchingOff: 'Off',
exitGame: 'Exit game',
exitGameHint: 'Leave the current session and return to scenario selection.',
saveAndExit: 'Save and exit',
exitWithoutSaving: 'Exit without saving',
notes: 'Notes',
saves: 'Saves',
continueRun: 'Continue game',
balanceEntry: 'Balance {credits}',
endingCollection: 'Ending collection',
narrator: 'Narrator',
npcName: 'Woman in the room',
npcLine: '"{line}"',
notStarted: 'Not started',
startHint: 'Select Start to enter the old house.',
debug: 'Debug stats',
debugOnly: 'Debug only',
waiting: 'Ready.',
modeAct: 'Do',
modeSay: 'Say',
inputMethod: 'Input method',
textInputMode: 'Text',
voiceInputMode: 'Voice',
choiceGroupLabel: 'Story choices',
inputPlaceholder: 'Example: I lift the bronze mirror toward her and the portrait.',
mobileInputPlaceholder: 'Say or do something…',
voiceStart: 'Start voice input',
voiceStop: 'Stop voice input',
voiceListening: 'Listening…',
voiceRecognizing: 'Recognizing…',
voiceUnsupported: 'Voice input is not supported in this browser',
voicePermissionDenied: 'Microphone access was denied. Check your browser permissions',
voiceMicrophoneUnavailable: 'No available microphone was found',
voiceNoSpeech: 'No speech was recognized. Try again',
voiceNoAudioCaptured: 'No microphone audio was captured. Check your browser permissions',
voiceRecognitionFailed: 'Voice recognition failed. Try again',
voiceHoldToTalk: 'Hold to talk',
voicePreparing: 'Preparing microphone…',
voiceShortcutLabel: 'Shortcut',
voiceReleaseSend: 'Release to send',
voiceSlideCancel: 'Slide up to cancel',
voiceReleaseCancel: 'Release to cancel',
voiceUnderstanding: 'Understanding your command…',
ending: 'Ending',
endingLabel: 'Ending',
endingLine: 'Ending: {title}\n{description}',
endingSummary: 'Ending summary',
endingPoem: 'Ending poem',
endingFallbackPoem: '...',
endingBackgroundLoadFailed: 'The ending scene failed to load. Please refresh and try again.',
recordSeal: 'Case',
recordTitle: 'Constable\'s report',
recordKicker: 'County archive',
recordMeta: 'After the rainy night',
continue: 'Continue',
enterNextEpisode: 'Enter next episode',
viewCurrentSeasonStory: 'View this season',
played: 'Played',
nextPage: 'Next page',
statusCreating: 'Preparing your story…',
statusStarted: 'Game started. Enter dialogue or an action.',
statusHistoryRun: 'You are viewing a previous game. Continuing will follow your latest saved progress.',
statusKp: 'The Keeper is evaluating your action…',
statusDone: 'Turn complete.',
contentFiltered: 'This action cannot be described. Try acting another way.',
turnFailed: 'We couldn\'t complete this step. Please try again.',
audioFailed: 'Speech playback failed. Please try again.',
actionRejected: 'That action cannot take effect yet. Try another approach.',
statusEnded: 'Game ended.',
statusEndingText: 'Writing your ending…',
statusEndingLocked: 'Ending determined. Preparing the finale…',
statusNpcAction: 'A character is taking action. The Keeper is evaluating the outcome…',
statusNpcDone: 'NPC responded.',
clueGained: 'Clue gained',
landscapeTitle: 'Rotate your device',
landscapeBody: 'This story is designed for landscape play so the full scene stays visible.',
stats: {
suspicion: 'Suspicion',
charm: 'Charm',
fear: 'Fear',
},
guide: {
label: 'Guide',
skip: 'Skip',
next: 'Next',
done: 'Got it',
optionsTitle: 'Choose a suggestion',
optionsText: 'After a character responds, two suggested choices appear below. Select Say or Do to continue.',
keyboardTitle: 'Switch to keyboard input',
keyboardText: 'Voice input is the default. Select the keyboard button whenever you want to type what you say or do.',
inputTitle: 'Choose Say or Do',
inputText: 'Before typing, choose whether you are speaking or acting. The Keeper will evaluate either choice.',
notesTitle: 'Check notes',
notesText: 'The notes button records discovered clues and unlocked endings.',
goalTitle: 'Survive',
goalText: 'This is more than a conversation. Listen carefully, explore the room, and investigate clues and anything unusual.',
videoTitle: 'Complete the story and turn your adventure into a video of your own',
videoExampleLabel: 'Example video',
videoExampleNotice: 'This video is only an example and does not represent the content generated from your playthrough.',
videoLoading: 'Loading an example video…',
videoUnavailable: 'This story does not have an example video yet.',
videoDone: 'Start my story',
},
openingOptions: {
say: 'Sorry, I did not mean to break in. Something outside was chasing me.',
act: 'Keep your voice low and inspect the doors, windows, lamp, and her position.',
},
},
panels: { clues: 'Clues', conclusions: 'Conclusions', info: 'Info', inventory: 'Inventory', events: 'Events', stats: 'Stats', flags: 'Flags', objects: 'Objects', npcs: 'NPCs', endingRecords: 'Unlocked endings' },
standalone: {
summary: 'Summary',
availableClues: 'Available clues',
recentTurns: 'Recent turns',
audit: {
title: 'AI audit',
subtitle: 'Costs and errors for Keeper, character, ending, and text-to-speech calls.',
totalCalls: 'Calls',
failedCalls: 'Failed',
tokens: 'Input/output tokens',
cost: 'Cost',
roles: 'Role summary',
models: 'Model summary',
recentCalls: 'Recent calls',
empty: 'No AI calls recorded.',
calls: '{count} calls',
tokenPair: 'Tokens: {tokens}',
},
state: {
title: 'State view',
waiting: 'Waiting for game data.',
noRun: 'No game ID was provided. Open this page from a game or enter an ID manually.',
loading: 'Loading game {runId}…',
loaded: 'Game {runId} loaded',
},
notes: {
title: 'Notes',
waiting: 'Loading notes.',
noRun: 'No current save found. Start or continue a run first.',
loading: 'Loading notes for game {runId}…',
loaded: 'Notes for game {runId} loaded',
unavailable: 'Notes for this game are temporarily unavailable.',
emptyClues: 'No clues yet',
emptyConclusions: 'No conclusions yet',
emptyEndings: 'No endings yet',
tabs: 'Note categories',
list: 'Entry list',
detail: 'Entry detail',
locked: 'Locked',
lockedHint: 'This ending has not been recorded yet.',
recorded: 'Recorded',
importance: { minor: 'Minor', major: 'Major', critical: 'Critical' },
relatedClues: 'Related clues',
noRelatedClues: 'No supporting clue has been found yet.',
},
},
editor: {
title: 'Scenario authoring',
pack: 'Pack',
section: 'Section',
raw: 'JSON',
structured: 'Structured editor',
debugJson: 'Debug JSON',
item: 'Item',
noStructuredFields: 'This section has no direct structured fields yet.',
updated: 'Saved',
assetUploaded: 'Asset uploaded to draft',
compiled: 'Draft compiled',
published: 'Version published',
rolledBack: 'Rolled back to version',
versions: 'Versions',
assets: 'Asset index',
current: 'Current',
currentVersion: 'Current version',
noPublishedVersion: 'Not published',
noVersions: 'No versions yet. Compile a draft first.',
noAssets: 'No asset index yet. Compile the asset manifest first.',
noFilteredAssets: 'No assets match the current filters.',
assetSearch: 'Search',
assetSearchPlaceholder: 'Tag, file, or objectKey',
assetTypeFilter: 'Type',
assetSourceFilter: 'Source',
assetFilterAll: 'All',
assetFilterResult: '{shown}/{total}',
assetSource: {
uploaded: 'Uploaded object',
static: 'Static file',
missing: 'Missing locator',
},
uploadFile: 'File',
uploadTag: 'Tag',
uploadTagPlaceholder: 'opening_cg',
uploadType: 'Type',
noUploadFile: 'No file selected',
},
waitingList: {
eyebrow: 'Early access',
apply: 'Join Waitlist',
useInvite: 'Use invite code',
invitedRegister: 'Have an invite? Create account',
title: 'Join the voko.world waitlist',
body: 'Leave your email. Once you are approved for early access, we will send you a personal invitation.',
displayName: 'Name',
displayNamePlaceholder: 'Optional',
useCase: 'Use case',
useCaseOptional: 'Use case (optional)',
useCasePlaceholder: 'For example: creator preview, team test, or live event',
useCaseRequired: 'Use case is required.',
consent: 'I agree to receive emails about my invite request and voko.world product updates.',
join: 'Join the waitlist',
joined: 'We received your request. We will email you when there is an update.',
joinedTitle: 'Request submitted',
changeEmail: 'Use another email',
accountAlreadyRegistered: 'This email is already registered. Please sign in.',
emailRequired: 'Email is required.',
consentRequired: 'Consent is required before joining the list.',
inviteEyebrow: 'Invite',
inviteTitle: 'Create your voko.world account',
inviteBody: 'This invite is linked to your email address. Set a password to finish creating your account.',
inviteMissing: 'No invite code was provided.',
inviteEnterCode: 'Enter the 8-character invite code you received to create an account.',
inviteValidate: 'Verify invite code',
inviteReenter: 'Enter another code',
inviteInvalid: 'This invite is invalid or expired.',
codeSent: 'Verification code sent.',
passwordMismatch: 'Passwords do not match.',
acceptInvite: 'Create account',
},
landing: { officialVideo: 'Watch the official video', officialVideoEyebrow: 'Meet VOKO', officialVideoTitle: 'Your kosmo is calling.', officialVideoBody: 'Create your own world, play through stories that call you back, and share every moment as video worth watching.', homeLabel: 'voko.world home', navHome: 'Home', navScripts: 'Stories', navVideos: 'Drama', featuredStories: 'Featured Games', featuredStoriesEyebrow: 'Start playing', endingCount: 'Endings: {count}', allStories: 'All Stories', dramaTitle: 'Drama', brandSub: 'Interactive stories', navLabel: 'Main navigation', navStories: 'Games', navGallery: 'Gallery', eyebrow: 'AI-powered visual role-playing', loadError: 'Could not load the home page. Please try again.', retry: 'Try again', emptyGames: 'No games are available yet.', scenarioFallback: 'Step into this world and reshape the story with every choice.', publicVideos: 'Player videos', publicGames: 'Public games', watchDrama: 'Watch story', enterPlay: 'Play now', comingSoon: 'Coming soon', carouselLabel: 'Featured games', previousGame: 'Previous game', nextGame: 'Next game', showcaseTitle: 'Stories shaped by players', showcaseBody: 'Watch the endings other players reached, then step into the game and create your own.', searchLabel: 'Search stories', searchPlaceholder: 'Search by game or episode title', sortLabel: 'Sort stories', latest: 'Latest', popular: 'Popular', videoCount: 'Videos: {count}', levelCount: 'Episodes: {count}', levelNumber: 'Episode {count}', singleLevel: 'Standalone story', publicGame: 'Public game', noSearchResults: 'No stories match your search', clearSearch: 'Clear filters', finalEyebrow: 'Your story awaits', finalTitle: 'Who will you become?', finalBody: 'Cross the threshold and make your first choice.', begin: 'Begin your story', footer: '© 2026 voko.world', earlyAccess: 'Early access', footerSocial: 'Community', footerLegal: 'Legal & contact', privacy: 'Privacy Policy', terms: 'Terms of Service', contact: 'Contact us' },
landingIntro: { title: 'voko.world', apply: 'Request access', watchVideo: 'Watch stories', start: 'Start game', continue: 'Continue game', viewJourney: 'Play history', continueProgress: 'Season {season} · {scenario}', storyLine: 'Storyline', viewMore: 'View more' },
landingCarousel: { introEyebrow: 'Generative drama game', introSummary: 'Watch a short drama, step into the role, and let every choice reshape the story.', dramaEyebrow: 'Featured drama {count}', controls: 'Home carousel', goToSlide: 'Go to slide {count}' },
landingCinema: { navHome: 'Home', navDramas: 'Game', navPlayerStories: 'Dramas', navMy: 'My', featuredDramas: 'Games', playerStories: 'Dramas' },
searchPage: { placeholder: 'Search stories, dramas, or accounts', popular: 'Popular Searches', history: 'Recent Searches', clearHistory: 'Clear', resultTypes: 'Search result types', all: 'All', accounts: 'Account', accountStats: '{titles} titles · {plays} plays' },
storyTree: {
eyebrow: 'Season storylines',
subtitle: 'Each line represents a season. Each node represents an episode.',
root: 'Story',
branch: 'Branch',
active: 'In progress',
completed: 'Complete',
locked: 'Not reached',
empty: 'No season storylines yet.',
loadError: 'Could not load your season storylines.',
navigation: 'Season range',
range: 'Seasons {first}–{last} · Total: {total}',
newer: 'Previous',
older: 'Next',
startPlaying: 'Play now',
turnProgress: 'Turn {turn}',
continuePlaying: 'Continue playing',
playLog: 'Play history',
seasonEnding: 'Season ending',
currentSeason: 'Current season',
logLoadError: 'Could not load your play history.',
logEmpty: 'There is no play history for this season yet.',
turnNumber: 'Turn {turn}',
playerSaid: 'You said',
playerDid: 'You did',
storyOpening: 'Story opening',
turnVideos: 'Matched videos',
playActionVideo: 'Action video',
playPlayerVideo: 'Player video',
playNpcVideo: 'NPC video',
replayTurn: 'Replay',
replayTurnTitle: 'Turn {turn} replay',
replayProgress: 'Segment {current}/{total}',
actionSegment: 'Action',
playerSpeechSegment: 'Player speech',
npcSpeechSegment: 'NPC speech',
pauseReplay: 'Pause',
continueReplay: 'Continue',
restartReplay: 'Replay again',
exportReplay: 'Export full replay',
replayQueued: 'Queued',
replayExporting: 'Exporting',
playFullReplay: 'Play full replay',
retryReplayExport: 'Retry export',
downloadReplay: 'Download MP4',
downloadSegment: 'Download this segment',
downloadTurn: 'Download full turn',
turnExporting: 'Preparing turn video',
turnExportError: 'Turn video export failed.',
segmentExporting: 'Preparing segment',
segmentExportError: 'Segment export failed.',
fullReplayTitle: '{title} · Full replay',
replayExportError: 'Full replay export failed.',
videoLoadError: 'The video could not be loaded.',
tools: 'Search, filter, and sort season storylines',
search: 'Search',
searchPlaceholder: 'Search episodes, endings, or recent actions',
filter: 'Status',
filterAll: 'All',
filterInProgress: 'In progress',
filterCompleted: 'Complete',
sort: 'Sort',
sortRecent: 'Recently played',
sortSeasonDesc: 'Newest season first',
sortSeasonAsc: 'Oldest season first',
resultCount: 'Seasons: {count}',
noResults: 'No season storylines match your filters.',
canvasHint: 'Drag or scroll horizontally to explore more episodes',
canvasLabel: 'Scrollable season storyline',
storySwitch: 'Story',
},
storyTreeCurrentSeasonHint: 'Latest progress',
storyTreeViewDetails: 'View Season {number} details',
storyTreeDelete: { action: 'Delete branch', confirm: 'Delete Season {number} and all branches created from it? This cannot be undone.', deleting: 'Deleting…', error: 'Could not delete this branch.' },
storyTreeVideoUnavailable: 'Video unavailable',
storyTreeFork: { action: 'Play again from here', confirm: 'A new story branch will be created from turn {turn}. Your current story history will not be changed.', failed: 'Failed to create the new story branch. Please try again.', creating: 'Creating…' },
runVideoGeneration: { title: 'Generate full video', description: 'Generate a full video from this run. Points are charged after confirmation and refunded if generation fails.', generate: 'Generate video', regenerate: 'Generate again', retry: 'Try again', queued: 'Queued', running: 'Generating', price: 'Points required', balance: 'Current balance', balanceAfter: 'Balance after charge' },
landingShowcase: { title: 'Lives Unfold', body: 'Witness the paths chosen by other players, then enter the story and write your own ending.' },
landingMetrics: { playCount: 'Plays', likeCount: 'Likes' },
landingCard: { level: 'Episode', searchPlaceholder: 'Find a story, video, or player', scriptFilter: 'Drama', scriptAll: 'All drama', playerVideoCount: 'Public videos: {count}', publishedAt: 'Published', publishedAll: 'Any time', publishedToday: 'Today', publishedWeek: 'Past 7 days', publishedMonth: 'Past 30 days', expandSynopsis: 'Show more', collapseSynopsis: 'Show less' },
playerPage: { loadError: 'Could not load this player\'s videos.', notFound: 'Player not found or has no public videos.', searchPlaceholder: 'Search dramas, levels, or season titles' },
season: { choose: 'Choose a season', chooseBody: 'This episode will continue the story in the selected season.', number: 'Season {number}', empty: 'Complete the previous episode in a season before continuing.' },
status: { loading: 'Loading…', empty: 'Nothing here yet', error: 'Could not load data', streaming: 'In progress' },
storiesPage: { tags: 'Story tags', allTags: 'All', swipeForNextStory: 'Swipe up for the next story' },
creator: { nav: 'Create', queued: 'Queued for review', attachmentTypeError: 'Upload one TXT, PDF, Word, PNG, JPG, or WEBP file.', attachmentSizeError: 'The attachment must be no larger than 10 MB.', attachmentReadError: 'The attachment could not be read.', generationFailed: 'Generation failed', reviewRejected: 'Review rejected', published: 'Published', archived: 'Archived', composerTitle: 'What kind of world will you create today?', submitIdea: 'Submit idea', examplesNote: 'Real published games shown only as layout references.', exampleStatus: 'Published example', projectsEyebrow: 'Visual Game Creator', projectsTitle: 'My games', projectsBody: 'Manage your games and follow their generation and review status.', newGame: 'Create new game', draftStatus: 'Draft', continueEditing: 'Continue editing', generatedScopes: 'generation items', emptyTitle: 'Create your first game', emptyBody: 'Start with an idea, script, or world setting. The creator system will turn it into a playable game.', eyebrow: 'Visual Game Creator', title: 'Turn your story into a playable world', subtitle: 'Share an idea, script, or world setting. The creator system will design the game, produce its media, validate every route, and send it for review.', stepIdea: 'Idea', stepGenerate: 'Generate', stepReview: 'Review', ideaTitle: 'Describe your game', ideaBody: 'Write naturally. You do not need to understand game schemas or state machines.', ideaLabel: 'Idea or script', ideaHint: 'Include the world, characters, conflict, and the experience you want.', ideaPlaceholder: 'A traveler wakes in an inn where every guest remembers a different version of last night…', ideaError: 'Please enter at least 20 characters.', languageLabel: 'Game language', languages: { zh: 'Chinese', en: 'English', ja: 'Japanese', ko: 'Korean' }, scopeTitle: 'Generate this time', scopeStory: 'Story & levels', scopeStoryBody: 'Characters, quests, states, and endings', scopeVisuals: 'Visual media', scopeVisualsBody: 'Characters, scenes, props, and videos', scopeVoice: 'Voice', scopeVoiceBody: 'Player, NPC, and ending narration', scopeError: 'Select at least one generation item.', assetsTitle: 'Reference material', assetsBody: 'Optional references help keep characters and worlds consistent.', uploadAction: 'Add reference files', uploadHint: 'One file · TXT, PDF, Word, PNG, JPG or WEBP · up to 10 MB', supportedTitle: 'Supported material', supportedBody: 'Character, scene, prop, and style images; scripts and world documents; video or audio references.', removeFile: 'Remove {name}', saved: 'Draft saved on this device.', draftNote: 'This first version saves a local draft. Generation will be connected next.', save: 'Save draft' },
labels: { scenario: 'Scenario', asset: 'Asset', mode: 'Mode', phase: 'Phase', location: 'Location', noRun: 'Not started', none: 'None' },
}