Fully implement i18n

This commit is contained in:
Elijah R 2024-04-09 15:06:10 -04:00
parent 24622e65eb
commit 9d60b44073
5 changed files with 388 additions and 155 deletions

View File

@ -22,14 +22,14 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">QEMU Monitor</h5>
<h5 id="qemuModalHeader" class="modal-title"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<textarea id="qemuMonitorOutput" readonly="" class="form-control"></textarea>
<div class="input-group">
<input type="text" id="qemuMonitorInput" class="form-control" placeholder="Command"/>
<button class="btn btn-outline-secondary btn-primary" type="button" id="qemuMonitorSendBtn">Send</button>
<button class="btn btn-outline-secondary btn-primary" type="button" id="qemuMonitorSendBtn"></button>
</div>
</div>
</div>
@ -39,35 +39,11 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h1>Welcome to CollabVM</h1>
</div>
<div class="modal-body">
<p>Before continuing, please familiarize yourself with our rules:</p>
<h3>R1. Don't break the law.</h3>
Do not use CollabVM or CollabVM's network to violate United States federal law, New York state law, or international law. If CollabVM becomes aware a crime has been committed through its service, you will be immediately banned, and your activities may be reported to the authorities if necessary.<br><br>CollabVM is required by law to notify law enforcement agencies if it becomes aware of the presence of child pornography on, or being transmitted through its network.<br><br>COPPA is also enforced, please do not use CollabVM if you are under the age of 13 years old.
<h3>R2. No running DoS/DDoS tools.</h3>
Do not use CollabVM to DoS/DDoS an indivdiual, business, company, or anyone else.
<h3>R3. No spam distribution.</h3>
Do not spam any emails using this service or push spam in general.
<h3>R4. Do not abuse any exploits.</h3>
Do not abuse any exploits, additionally if you see someone abusing exploits or you need to report one, please contact me at: computernewbab@gmail.com
<h3>R5. Don't impersonate other users.</h3>
Do not impersonate other members of CollabVM. If caught, you'll be temporarily disconnected, and banned if necessary.
<h3>R6. One vote per person.</h3>
Do not use any methods or tools to bypass the vote restriction. Only one vote per person is allowed, no matter what. Anybody who is caught doing this will be banned.
<h3>R7. No Remote Administration Tools.</h3>
Do not use any remote administration tools (ex: DarkComet, NanoCore, Anydesk, TeamViewer, Orcus, etc.)
<h3>R8. No bypassing CollabNet.</h3>
Do not attempt to bypass the blocking provided by CollabNet, especially if it is being used to break Rule 1, Rule 2, or Rule 7 (or run stupid over-used things).
<h3>R9. No performing destructive actions constantly.</h3>
Any user may not destroy the VM (rendering it unusable constantly), install/reinstall the operating system (except on VM7 or VM8), or run bots that do such. This includes bots that spam massive amounts of keyboard/mouse input ("kitting").
<h3>R10. No Cryptomining</h3>
Attempting to mine cryptocurrency on the VMs will result in a kick, and then a permanent ban if you keep attempting. Besides, it's not like you're gonna make any money off it.
<h3>NSFW Warning</h3>
Please note that NSFW content is allowed on our anarchy VM (VM0b0t), and is viewed regularly. In addition, while we give a good effort to keep NSFW off the main VMs, people will occasionally slip it through.
<h1 id="welcomeModalHeader"></h1>
</div>
<div class="modal-body" id="welcomeModalBody"></div>
<div class="modal-footer">
<button type="button" id="welcomeModalDismiss" class="btn btn-primary" data-bs-dismiss="modal">Understood</button>
<button type="button" id="welcomeModalDismiss" class="btn btn-primary" data-bs-dismiss="modal"></button>
</div>
</div>
</div>
@ -81,18 +57,18 @@
</div>
<div class="modal-body">
<div class="alert alert-danger alert-dismissible" id="badPasswordAlert" role="alert">
Incorrect password.
<span id="badPasswordAlertText"></span>
<button type="button" class="btn-close" aria-label="Close" id="incorrectPasswordDismissBtn"></button>
</div>
<div class="input-group">
<input type="hidden" name="username" id="adminInputVMID"/>
<span class="input-group-text">Password</span>
<input id="adminPassword" type="password" class="form-control" placeholder="Password" name="password"/>
<span class="input-group-text" id="loginModalPasswordText"></span>
<input id="adminPassword" type="password" class="form-control" name="password"/>
</div>
</div>
<div class="modal-footer">
<button type="button" id="loginButton" class="btn btn-primary">Login</button>
<button type="button" id="loginButton" class="btn btn-primary"></button>
</div>
</div>
</div>
@ -101,7 +77,7 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="accountModalTitle">Login</h5>
<h5 class="modal-title" id="accountModalTitle"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@ -115,28 +91,28 @@
</div>
<div id="accountLoginSection">
<form id="accountLoginForm">
<label for="accountLoginUsername">Username</label><br/>
<input id="accountLoginUsername" type="text" class="form-control" placeholder="Username" name="username" required/><br>
<label for="accountLoginPassword">Password</label><br/>
<input id="accountLoginPassword" type="password" class="form-control" placeholder="Password" name="password" required><br>
<label for="accountLoginUsername" id="accountLoginUsernameLabel"></label><br/>
<input id="accountLoginUsername" type="text" class="form-control" name="username" required/><br>
<label for="accountLoginPassword" id="accountLoginPasswordLabel"></label><br/>
<input id="accountLoginPassword" type="password" class="form-control" name="password" required><br>
<div id="accountLoginCaptcha"></div>
<button type="submit" class="btn btn-primary">Login</button> <button type="button" class="btn btn-secondary" id="accountForgotPasswordButton">Forgot Password</button>
<button type="submit" class="btn btn-primary" id="accountModalLoginBtn"></button> <button type="button" class="btn btn-secondary" id="accountForgotPasswordButton"></button>
</form>
</div>
<div id="accountRegisterSection">
<form id="accountRegisterForm">
<label for="accountRegisterEmail">E-Mail</label><br/>
<input id="accountRegisterEmail" type="email" class="form-control" placeholder="E-Mail" name="email" required/><br>
<label for="accountRegisterUsername">Username</label><br/>
<input id="accountRegisterUsername" type="text" class="form-control" placeholder="Username" name="username" required/><br>
<label for="accountRegisterPassword">Password</label><br/>
<input id="accountRegisterPassword" type="password" class="form-control" placeholder="Password" name="password" required><br>
<label for="accountRegisterConfirmPassword">Confirm Password</label><br/>
<input id="accountRegisterConfirmPassword" type="password" class="form-control" placeholder="Confirm Password" name="confirmpassword" required><br>
<label for="accountRegisterDateOfBirth">Date of Birth</label><br/>
<label for="accountRegisterEmail" id="accountRegisterEmailLabel"></label><br/>
<input id="accountRegisterEmail" type="email" class="form-control" name="email" required/><br>
<label for="accountRegisterUsername" id="accountRegisterUsernameLabel"></label><br/>
<input id="accountRegisterUsername" type="text" class="form-control" name="username" required/><br>
<label for="accountRegisterPassword" id="accountRegisterPasswordLabel"></label><br/>
<input id="accountRegisterPassword" type="password" class="form-control" name="password" required><br>
<label for="accountRegisterConfirmPassword" id="accountRegisterConfirmPasswordLabel"></label><br/>
<input id="accountRegisterConfirmPassword" type="password" class="form-control" name="confirmpassword" required><br>
<label for="accountRegisterDateOfBirth" id="accountRegisterDateOfBirthLabel"></label><br/>
<input id="accountRegisterDateOfBirth" type="date" class="form-control" name="dateofbirth" required><br/>
<div id="accountRegisterCaptcha"></div>
<button type="submit" class="btn btn-primary">Register</button>
<button type="submit" class="btn btn-primary" id="accountModalRegisterBtn"></button>
</form>
</div>
<div id="accountVerifyEmailSection">
@ -144,50 +120,50 @@
<i class="fa-solid fa-envelope" style="font-size: 12rem"></i>
<p id="accountVerifyEmailText"></p>
<form id="accountVerifyEmailForm">
<label for="accountVerifyEmailCode">Code</label><br>
<input id="accountVerifyEmailCode" type="text" class="form-control" name="code" placeholder="Code" required><br>
<label for="accountVerifyEmailPassword">Your password</label><br>
<input id="accountVerifyEmailPassword" type="password" class="form-control" placeholder="Password" name="password" required/><br/>
<button type="submit" class="btn btn-primary">Verify</button>
<label for="accountVerifyEmailCode" id="accountVerifyEmailCodeLabel"></label><br>
<input id="accountVerifyEmailCode" type="text" class="form-control" name="code" required><br>
<label for="accountVerifyEmailPassword" id="accountVerifyEmailPasswordLabel"></label><br>
<input id="accountVerifyEmailPassword" type="password" class="form-control" name="password" required/><br/>
<button type="submit" class="btn btn-primary" id="accountModalVerifyEmailBtn"></button>
</form>
</center>
</div>
<div id="accountSettingsSection">
<form id="accountSettingsForm">
<label for="accountSettingsEmail">E-Mail</label><br>
<input id="accountSettingsEmail" type="email" class="form-control" placeholder="E-Mail" name="email" required/><br/>
<label for="accountSettingsUsername">Username</label><br>
<input id="accountSettingsUsername" type="text" class="form-control" placeholder="Username" name="username" required/><br/>
<label for="accountSettingsNewPassword">New Password</label>
<input id="accountSettingsNewPassword" type="password" class="form-control" placeholder="New Password" name="password"/><br/>
<label for="accountSettingsConfirmNewPassword">Confirm New Password</label>
<input id="accountSettingsConfirmNewPassword" type="password" class="form-control" placeholder="Confirm New Password" name="confirmpassword"/><br/>
<label for="accountSettingsCurrentPassword">Current Password</label>
<input id="accountSettingsCurrentPassword" type="password" class="form-control" placeholder="Current Password" name="currentpassword" required/><br/>
<button type="submit" class="btn btn-primary">Update</button>
<label for="accountSettingsEmail" id="accountSettingsEmailLabel"></label><br>
<input id="accountSettingsEmail" type="email" class="form-control" name="email" required/><br/>
<label for="accountSettingsUsername" id="accountSettingsUsernameLabel"></label><br>
<input id="accountSettingsUsername" type="text" class="form-control" name="username" required/><br/>
<label for="accountSettingsNewPassword" id="accountSettingsNewPasswordLabel"></label>
<input id="accountSettingsNewPassword" type="password" class="form-control" name="password"/><br/>
<label for="accountSettingsConfirmNewPassword" id="accountSettingsConfirmNewPasswordLabel"></label>
<input id="accountSettingsConfirmNewPassword" type="password" class="form-control" name="confirmpassword"/><br/>
<label for="accountSettingsCurrentPassword" id="accountSettingsCurrentPasswordLabel"></label>
<input id="accountSettingsCurrentPassword" type="password" class="form-control" name="currentpassword" required/><br/>
<button type="submit" class="btn btn-primary" id="updateAccountSettingsBtn"></button>
</form>
</div>
<div id="accountResetPasswordSection">
<form id="accountResetPasswordForm">
<label for="accountResetPasswordEmail">E-Mail</label><br>
<input id="accountResetPasswordEmail" type="email" class="form-control" placeholder="E-Mail" name="email" required/><br/>
<label for="accountResetPasswordUsername">Username</label>
<input id="accountResetPasswordUsername" type="text" class="form-control" placeholder="Username" name="username" required/><br/>
<label for="accountResetPasswordEmail" id="accountResetPasswordEmailLabel"></label><br>
<input id="accountResetPasswordEmail" type="email" class="form-control" name="email" required/><br/>
<label for="accountResetPasswordUsername" id="accountResetPasswordUsernameLabel"></label>
<input id="accountResetPasswordUsername" type="text" class="form-control" name="username" required/><br/>
<div id="accountResetPasswordCaptcha"></div>
<button type="submit" class="btn btn-primary">Reset</button>
<button type="submit" class="btn btn-primary" id="accountResetPasswordBtn"></button>
</div>
<div id="accountResetPasswordVerifySection">
<center>
<i class="fa-solid fa-envelope" style="font-size: 12rem"></i>
<p id="accountVerifyPasswordResetText"></p>
<form id="accountResetPasswordVerifyForm">
<label for="accountResetPasswordCode">Code</label><br>
<input id="accountResetPasswordCode" type="text" class="form-control" name="code" placeholder="Code" required><br>
<label for="accountResetPasswordNewPassword">New Password</label><br>
<input id="accountResetPasswordNewPassword" type="password" class="form-control" placeholder="New Password" name="password" required/><br/>
<label for="accountResetPasswordConfirmNewPassword">Confirm New Password</label><br>
<input id="accountResetPasswordConfirmNewPassword" type="password" class="form-control" placeholder="Confirm New Password" name="confirmpassword" required/><br/>
<button type="submit" class="btn btn-primary">Reset</button>
<label for="accountResetPasswordCode" id="accountResetPasswordCodeLabel"></label><br>
<input id="accountResetPasswordCode" type="text" class="form-control" name="code" required><br>
<label for="accountResetPasswordNewPassword" id="accountResetPasswordNewPasswordLabel"></label><br>
<input id="accountResetPasswordNewPassword" type="password" class="form-control" name="password" required/><br/>
<label for="accountResetPasswordConfirmNewPassword" id="accountResetPasswordConfirmNewPasswordLabel"></label><br>
<input id="accountResetPasswordConfirmNewPassword" type="password" class="form-control" name="confirmpassword" required/><br/>
<button type="submit" class="btn btn-primary" id="accountResetPasswordVerifyBtn"></button>
</form>
</center>
</div>
@ -230,13 +206,13 @@
</ul>
<div class="navbar-text dropdown">
<a class="nav-link dropdown-toggle" href="#" id="accountDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa-solid fa-user"></i> <span id="accountDropdownUsername">Not Logged in</span>
<i class="fa-solid fa-user"></i> <span id="accountDropdownUsername"></span>
</a>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="accountDropdownMenuLink">
<a class="dropdown-item" href="#" id="accountLoginButton">Log in</a>
<a class="dropdown-item" href="#" id="accountRegisterButton">Register</a>
<a class="dropdown-item" href="#" id="accountSettingsButton">Account Settings</a>
<a class="dropdown-item" href="#" id="accountLogoutButton">Logout</a>
<a class="dropdown-item" href="#" id="accountLoginButton"></a>
<a class="dropdown-item" href="#" id="accountRegisterButton"></a>
<a class="dropdown-item" href="#" id="accountSettingsButton"></a>
<a class="dropdown-item" href="#" id="accountLogoutButton"></a>
</div>
</div>
</div>
@ -259,19 +235,19 @@
</div>
<div id="btns">
<button class="btn btn-secondary" id="takeTurnBtn"><i class="fa-solid fa-computer-mouse"></i> <span id="turnBtnText"></span></button>
<button class="btn btn-secondary" id="oskBtn"><i class="fa-solid fa-keyboard"></i> Keyboard</button>
<button class="btn btn-secondary" id="oskBtn"><i class="fa-solid fa-keyboard"></i> <span id="oskBtnText"></span></button>
<button class="btn btn-secondary" id="changeUsernameBtn"><i class="fa-solid fa-signature"></i> <span id="changeUsernameBtnText"></span></button>
<button class="btn btn-secondary" id="voteResetButton"><i class="fa-solid fa-rotate-left"></i> <span id="voteForResetBtnText"></span></button>
<button class="btn btn-secondary" id="screenshotButton"><i class="fa-solid fa-camera"></i> <span id="screenshotBtnText"></span></button>
<button class="btn btn-secondary" id="ctrlAltDelBtn"><i class="fa-solid fa-gear"></i> Ctrl+Alt+Del</button>
<button class="btn btn-secondary" id="ctrlAltDelBtn"><i class="fa-solid fa-gear"></i> <span id="ctrlAltDelBtnText"></span></button>
<div id="staffbtns">
<button class="btn btn-secondary" id="restoreBtn"><i class="fa-solid fa-rotate-left"></i> Restore</button>
<button class="btn btn-secondary" id="rebootBtn"><i class="fa-solid fa-power-off"></i> Reboot</button>
<button class="btn btn-secondary" id="clearQueueBtn"><i class="fa-solid fa-eraser"></i> Clear Turn Queue</button>
<button class="btn btn-secondary" id="bypassTurnBtn"><i class="fa-solid fa-forward"></i> Bypass Turn</button>
<button class="btn btn-secondary" id="restoreBtn"><i class="fa-solid fa-rotate-left"></i> <span id="restoreBtnText"></span></button>
<button class="btn btn-secondary" id="rebootBtn"><i class="fa-solid fa-power-off"></i> <span id="rebootBtnText"></span></button>
<button class="btn btn-secondary" id="clearQueueBtn"><i class="fa-solid fa-eraser"></i> <span id="clearQueueBtnText"></span></button>
<button class="btn btn-secondary" id="bypassTurnBtn"><i class="fa-solid fa-forward"></i> <span id="bypassTurnBtnText"></span></button>
<button class="btn btn-secondary" id="endTurnBtn"><i class="fa-solid fa-ban"></i> <span id="endTurnBtnText"></span></button>
<button class="btn btn-secondary" id="indefTurnBtn"><i class="fa-solid fa-infinity"></i> Indefinite Turn</button>
<button class="btn btn-secondary" id="qemuMonitorBtn" data-bs-toggle="modal" data-bs-target="#qemuMonitorModal"><i class="fa-solid fa-terminal"></i> QEMU Monitor</button>
<button class="btn btn-secondary" id="indefTurnBtn"><i class="fa-solid fa-infinity"></i> <span id="indefTurnBtnText"></span></button>
<button class="btn btn-secondary" id="qemuMonitorBtn" data-bs-toggle="modal" data-bs-target="#qemuMonitorModal"><i class="fa-solid fa-terminal"></i> <span id="qemuMonitorBtnText"></span></button>
</div>
</div>
<div class="osk-container d-none" id="osk-container">
@ -307,7 +283,7 @@
</table>
</div>
<div class="input-group">
<span class="input-group-text username-unregistered" id="username">Username</span>
<span class="input-group-text username-unregistered" id="username"></span>
<input type="text" class="form-control" id="chat-input"/>
<div class="input-group-text" id="xssCheckboxContainer">
<input class="form-check-input" type="checkbox" value="" id="xssCheckbox"/>

101
src/ts/fallbackLanguage.ts Normal file
View File

@ -0,0 +1,101 @@
import { Language } from "./i18n.js";
const fallbackLanguage : Language = {
"languageName": "English (US)",
"translatedLanguageName": "English (US)",
"flag": "🇺🇸",
"author": "Computernewb",
"stringKeys": {
"kGeneric_CollabVM": "CollabVM",
"kGeneric_Yes": "Yes",
"kGeneric_No": "No",
"kGeneric_Ok": "OK",
"kGeneric_Cancel": "Cancel",
"kGeneric_Send": "Send",
"kGeneric_Understood": "Understood",
"kGeneric_Username": "Username",
"kGeneric_Password": "Password",
"kGeneric_Login": "Log in",
"kGeneric_Register": "Register",
"kGeneric_EMail": "E-Mail",
"kGeneric_DateOfBirth": "Date of Birth",
"kGeneric_VerificationCode": "Verification Code",
"kGeneric_Verify": "Verify",
"kGeneric_Update": "Update",
"kGeneric_Logout": "Log out",
"kWelcomeModal_Header": "Welcome to CollabVM",
"kWelcomeModal_Body": "<p>Before continuing, please familiarize yourself with our rules:</p> <h3>R1. Don't break the law.</h3> Do not use CollabVM or CollabVM's network to violate United States federal law, New York state law, or international law. If CollabVM becomes aware a crime has been committed through its service, you will be immediately banned, and your activities may be reported to the authorities if necessary.<br><br>CollabVM is required by law to notify law enforcement agencies if it becomes aware of the presence of child pornography on, or being transmitted through its network.<br><br>COPPA is also enforced, please do not use CollabVM if you are under the age of 13 years old. <h3>R2. No running DoS/DDoS tools.</h3> Do not use CollabVM to DoS/DDoS an indivdiual, business, company, or anyone else. <h3>R3. No spam distribution.</h3> Do not spam any emails using this service or push spam in general. <h3>R4. Do not abuse any exploits.</h3> Do not abuse any exploits, additionally if you see someone abusing exploits or you need to report one, please contact me at: computernewbab@gmail.com <h3>R5. Don't impersonate other users.</h3> Do not impersonate other members of CollabVM. If caught, you'll be temporarily disconnected, and banned if necessary. <h3>R6. One vote per person.</h3> Do not use any methods or tools to bypass the vote restriction. Only one vote per person is allowed, no matter what. Anybody who is caught doing this will be banned. <h3>R7. No Remote Administration Tools.</h3> Do not use any remote administration tools (ex: DarkComet, NanoCore, Anydesk, TeamViewer, Orcus, etc.) <h3>R8. No bypassing CollabNet.</h3> Do not attempt to bypass the blocking provided by CollabNet, especially if it is being used to break Rule 1, Rule 2, or Rule 7 (or run stupid over-used things). <h3>R9. No performing destructive actions constantly.</h3> Any user may not destroy the VM (rendering it unusable constantly), install/reinstall the operating system (except on VM7 or VM8), or run bots that do such. This includes bots that spam massive amounts of keyboard/mouse input (\"kitting\"). <h3>R10. No Cryptomining</h3> Attempting to mine cryptocurrency on the VMs will result in a kick, and then a permanent ban if you keep attempting. Besides, it's not like you're gonna make any money off it. <h3>NSFW Warning</h3> Please note that NSFW content is allowed on our anarchy VM (VM0b0t), and is viewed regularly. In addition, while we give a good effort to keep NSFW off the main VMs, people will occasionally slip it through.",
"kSiteButtons_Home": "Home",
"kSiteButtons_FAQ": "FAQ",
"kSiteButtons_Rules": "Rules",
"kSiteButtons_DarkMode": "Dark Mode",
"kSiteButtons_LightMode": "Light Mode",
"kVM_UsersOnlineText": "Users Online:",
"kVM_TurnTimeTimer": "Turn expires in {0} seconds.",
"kVM_WaitingTurnTimer": "Waiting for turn in {0} seconds.",
"kVM_VoteCooldownTimer": "Please wait {0} seconds before starting another vote.",
"kVM_VoteForResetTitle": "Do you want to reset the VM?",
"kVM_VoteForResetTimer": "Vote ends in {0} seconds",
"kVMButtons_TakeTurn": "Take Turn",
"kVMButtons_EndTurn": "End Turn",
"kVMButtons_ChangeUsername": "Change Username",
"kVMButtons_Keyboard": "Keyboard",
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
"kVMButtons_VoteForReset": "Vote For Reset",
"kVMButtons_Screenshot": "Screenshot",
"kQEMUMonitor": "QEMU Monitor",
"kAdminVMButtons_PassVote": "Pass Vote",
"kAdminVMButtons_CancelVote": "Cancel Vote",
"kAdminVMButtons_Restore": "Restore",
"kAdminVMButtons_Reboot": "Reboot",
"kAdminVMButtons_ClearTurnQueue": "Clear Turn Queue",
"kAdminVMButtons_BypassTurn": "Bypass Turn",
"kAdminVMButtons_IndefiniteTurn": "Indefinite Turn",
"kAdminVMButtons_Ban": "Ban",
"kAdminVMButtons_Kick": "Kick",
"kAdminVMButtons_TempMute": "Temporary Mute",
"kAdminVMButtons_IndefMute": "Indefinite Mute",
"kAdminVMButtons_Unmute": "Unmute",
"kAdminVMButtons_GetIP": "Get IP",
"kVMPrompts_AdminChangeUsernamePrompt": "Enter new username for {0}:",
"kVMPrompts_AdminRestoreVMPrompt": "Are you sure you want to restore the VM?",
"kVMPrompts_EnterNewUsernamePrompt": "Enter a new username, or leave the field blank to be assigned a guest username",
"kError_UnexpectedDisconnection": "You have been disconnected from the server.",
"kError_UsernameTaken": "That username is already taken",
"kError_UsernameInvalid": "Usernames can contain only numbers, letters, spaces, dashes, underscores, and dots, and it must be between 3 and 20 characters.",
"kError_UsernameBlacklisted": "That username has been blacklisted.",
"kError_IncorrectPassword": "Incorrect password.",
"kAccountModal_Verify": "Verify E-Mail",
"kAccountModal_AccountSettings": "Account Settings",
"kAccountModal_ResetPassword": "Reset Password",
"kAccountModal_NewPassword": "New Password",
"kAccountModal_ConfirmNewPassword": "Confirm New Password",
"kAccountModal_CurrentPassword": "Current Password",
"kAccountModal_ConfirmPassword": "Confirm Password",
"kMissingCaptcha": "Please fill out the captcha.",
"kPasswordsMustMatch": "Passwords must match.",
"kAccountModal_VerifyText": "We sent an E-Mail to {0}. To verify your account, please enter the 8-digit code from the E-Mail below.",
"kAccountModal_VerifyPasswordResetText": "We sent an E-Mail to {0}. To reset your password, please enter the 8-digit code from the E-Mail below.",
"kAccountModal_PasswordResetSuccess": "Your password has been changed successfully. You can now log in with your new password.",
"kNotLoggedIn": "Not Logged in"
}
}
export default fallbackLanguage;

View File

@ -9,6 +9,21 @@ export enum I18nStringKey {
kGeneric_No = 'kGeneric_No',
kGeneric_Ok = 'kGeneric_Ok',
kGeneric_Cancel = 'kGeneric_Cancel',
kGeneric_Send = 'kGeneric_Send',
kGeneric_Understood = 'kGeneric_Understood',
kGeneric_Username = 'kGeneric_Username',
kGeneric_Password = 'kGeneric_Password',
kGeneric_Login = 'kGeneric_Login',
kGeneric_Register = 'kGeneric_Register',
kGeneric_EMail = 'kGeneric_EMail',
kGeneric_DateOfBirth = 'kGeneric_DateOfBirth',
kGeneric_VerificationCode = 'kGeneric_VerificationCode',
kGeneric_Verify = 'kGeneric_Verify',
kGeneric_Update = 'kGeneric_Update',
kGeneric_Logout = 'kGeneric_Logout',
kWelcomeModal_Header = 'kWelcomeModal_Header',
kWelcomeModal_Body = 'kWelcomeModal_Body',
kSiteButtons_Home = 'kSiteButtons_Home',
kSiteButtons_FAQ = 'kSiteButtons_FAQ',
@ -28,15 +43,33 @@ export enum I18nStringKey {
kVMButtons_TakeTurn = 'kVMButtons_TakeTurn',
kVMButtons_EndTurn = 'kVMButtons_EndTurn',
kVMButtons_ChangeUsername = 'kVMButtons_ChangeUsername',
kVMButtons_Keyboard = 'kVMButtons_Keyboard',
KVMButtons_CtrlAltDel = 'KVMButtons_CtrlAltDel',
kVMButtons_VoteForReset = 'kVMButtons_VoteForReset',
kVMButtons_Screenshot = 'kVMButtons_Screenshot',
// Admin VM buttons
kQEMUMonitor = 'kQEMUMonitor',
kAdminVMButtons_PassVote = 'kAdminVMButtons_PassVote',
kAdminVMButtons_CancelVote = 'kAdminVMButtons_CancelVote',
kAdminVMButtons_Restore = 'kAdminVMButtons_Restore',
kAdminVMButtons_Reboot = 'kAdminVMButtons_Reboot',
kAdminVMButtons_ClearTurnQueue = 'kAdminVMButtons_ClearTurnQueue',
kAdminVMButtons_BypassTurn = 'kAdminVMButtons_BypassTurn',
kAdminVMButtons_IndefiniteTurn = 'kAdminVMButtons_IndefiniteTurn',
kAdminVMButtons_Ban = 'kAdminVMButtons_Ban',
kAdminVMButtons_Kick = 'kAdminVMButtons_Kick',
kAdminVMButtons_TempMute = 'kAdminVMButtons_TempMute',
kAdminVMButtons_IndefMute = 'kAdminVMButtons_IndefMute',
kAdminVMButtons_Unmute = 'kAdminVMButtons_Unmute',
kAdminVMButtons_GetIP = 'kAdminVMButtons_GetIP',
// prompts
kVMPrompts_AdminChangeUsernamePrompt = 'kVMPrompts_AdminChangeUsernamePrompt',
kVMPrompts_AdminRestoreVMPrompt = 'kVMPrompts_AdminRestoreVMPrompt',
kVMPrompts_EnterNewUsernamePrompt = 'kVMPrompts_EnterNewUsernamePrompt',
// error messages
@ -45,14 +78,18 @@ export enum I18nStringKey {
kError_UsernameTaken = 'kError_UsernameTaken',
kError_UsernameInvalid = 'kError_UsernameInvalid',
kError_UsernameBlacklisted = 'kError_UsernameBlacklisted',
kError_IncorrectPassword = 'kError_IncorrectPassword',
// Auth
kAccountModal_Login = 'kAccountModal_Login',
kAccountModal_Register = 'kAccountModal_Register',
kAccountModal_Verify = 'kAccountModal_Verify',
kAccountModal_AccountSettings = 'kAccountModal_AccountSettings',
kAccountModal_ResetPassword = 'kAccountModal_ResetPassword',
kAccountModal_NewPassword = 'kAccountModal_NewPassword',
kAccountModal_ConfirmNewPassword = 'kAccountModal_ConfirmNewPassword',
kAccountModal_CurrentPassword = 'kAccountModal_CurrentPassword',
kAccountModal_ConfirmPassword = 'kAccountModal_ConfirmPassword',
kAccountModal_VerifyText = 'kAccountModal_VerifyText',
kAccountModal_VerifyPasswordResetText = 'kAccountModal_VerifyPasswordResetText',
kAccountModal_PasswordResetSuccess = 'kAccountModal_PasswordResetSuccess',
@ -63,7 +100,7 @@ export enum I18nStringKey {
}
// This models the JSON structure.
type Language = {
export type Language = {
languageName: string;
translatedLanguageName: string;
flag: string; // country flag, can be blank if not applicable. will be displayed in language dropdown
@ -91,50 +128,7 @@ type LanguagesJson = {
const fallbackId = '!!fallback';
// This language is provided in the webapp itself just in case language stuff fails
const fallbackLanguage: Language = {
languageName: 'Fallback',
translatedLanguageName: 'Fallback',
flag: 'no',
author: 'Computernewb',
stringKeys: {
kGeneric_CollabVM: 'CollabVM',
kGeneric_Yes: 'Yes',
kGeneric_No: 'No',
kGeneric_Ok: 'OK',
kGeneric_Cancel: 'Cancel',
kSiteButtons_Home: 'Home',
kSiteButtons_FAQ: 'FAQ',
kSiteButtons_Rules: 'Rules',
kVM_UsersOnlineText: 'Users Online:',
kVM_TurnTimeTimer: 'Turn expires in {0} seconds.',
kVM_WaitingTurnTimer: 'Waiting for turn in {0} seconds.',
kVM_VoteCooldownTimer: 'Please wait {0} seconds before starting another vote.',
kVM_VoteForResetTitle: 'Do you want to reset the VM?',
kVM_VoteForResetTimer: 'Vote ends in {0} seconds',
kVMButtons_TakeTurn: 'Take Turn',
kVMButtons_EndTurn: 'End Turn',
kVMButtons_ChangeUsername: 'Change Username',
kVMButtons_VoteForReset: 'Vote For Reset',
kVMButtons_Screenshot: 'Screenshot',
kAdminVMButtons_PassVoteButton: 'Pass Vote',
kAdminVMButtons_CancelVoteButton: 'Cancel Vote',
kVMPrompts_EnterNewUsernamePrompt: 'Enter a new username, or leave the field blank to be assigned a guest username',
kError_UnexpectedDisconnection: 'You have been disconnected from the server.',
kError_UsernameTaken: 'That username is already taken',
kError_UsernameInvalid: 'Usernames can contain only numbers, letters, spaces, dashes, underscores, and dots, and it must be between 3 and 20 characters.',
kError_UsernameBlacklisted: 'That username has been blacklisted.'
}
};
import fallbackLanguage from './fallbackLanguage.js';
interface StringKeyMap {
[k: string]: I18nStringKey;
@ -216,7 +210,15 @@ export class I18n {
homeBtnText: I18nStringKey.kSiteButtons_Home,
faqBtnText: I18nStringKey.kSiteButtons_FAQ,
rulesBtnText: I18nStringKey.kSiteButtons_Rules,
accountDropdownUsername: I18nStringKey.kNotLoggedIn,
accountLoginButton: I18nStringKey.kGeneric_Login,
accountRegisterButton: I18nStringKey.kGeneric_Register,
accountSettingsButton: I18nStringKey.kAccountModal_AccountSettings,
accountLogoutButton: I18nStringKey.kGeneric_Logout,
welcomeModalHeader: I18nStringKey.kWelcomeModal_Header,
welcomeModalBody: I18nStringKey.kWelcomeModal_Body,
welcomeModalDismiss: I18nStringKey.kGeneric_Understood,
usersOnlineText: I18nStringKey.kVM_UsersOnlineText,
@ -225,19 +227,121 @@ export class I18n {
voteNoBtnText: I18nStringKey.kGeneric_No,
changeUsernameBtnText: I18nStringKey.kVMButtons_ChangeUsername,
oskBtnText: I18nStringKey.kVMButtons_Keyboard,
ctrlAltDelBtnText: I18nStringKey.KVMButtons_CtrlAltDel,
voteForResetBtnText: I18nStringKey.kVMButtons_VoteForReset,
screenshotBtnText: I18nStringKey.kVMButtons_Screenshot,
// admin stuff
badPasswordAlertText: I18nStringKey.kError_IncorrectPassword,
loginModalPasswordText: I18nStringKey.kGeneric_Password,
loginButton: I18nStringKey.kGeneric_Login,
passVoteBtnText: I18nStringKey.kAdminVMButtons_PassVote,
cancelVoteBtnText: I18nStringKey.kAdminVMButtons_CancelVote,
endTurnBtnText: I18nStringKey.kVMButtons_EndTurn
endTurnBtnText: I18nStringKey.kVMButtons_EndTurn,
qemuMonitorBtnText: I18nStringKey.kQEMUMonitor,
qemuModalHeader: I18nStringKey.kQEMUMonitor,
qemuMonitorSendBtn: I18nStringKey.kGeneric_Send,
restoreBtnText: I18nStringKey.kAdminVMButtons_Restore,
rebootBtnText: I18nStringKey.kAdminVMButtons_Reboot,
clearQueueBtnText: I18nStringKey.kAdminVMButtons_ClearTurnQueue,
bypassTurnBtnText: I18nStringKey.kAdminVMButtons_BypassTurn,
indefTurnBtnText: I18nStringKey.kAdminVMButtons_IndefiniteTurn,
// Account modal
accountLoginUsernameLabel: I18nStringKey.kGeneric_Username,
accountLoginPasswordLabel: I18nStringKey.kGeneric_Password,
accountModalLoginBtn: I18nStringKey.kGeneric_Login,
accountForgotPasswordButton: I18nStringKey.kAccountModal_ResetPassword,
accountRegisterEmailLabel: I18nStringKey.kGeneric_EMail,
accountRegisterUsernameLabel: I18nStringKey.kGeneric_Username,
accountRegisterPasswordLabel: I18nStringKey.kGeneric_Password,
accountRegisterConfirmPasswordLabel: I18nStringKey.kAccountModal_ConfirmPassword,
accountRegisterDateOfBirthLabel: I18nStringKey.kGeneric_DateOfBirth,
accountModalRegisterBtn: I18nStringKey.kGeneric_Register,
accountVerifyEmailCodeLabel: I18nStringKey.kGeneric_VerificationCode,
accountVerifyEmailPasswordLabel: I18nStringKey.kGeneric_Password,
accountModalVerifyEmailBtn: I18nStringKey.kGeneric_Verify,
accountSettingsEmailLabel: I18nStringKey.kGeneric_EMail,
accountSettingsUsernameLabel: I18nStringKey.kGeneric_Username,
accountSettingsNewPasswordLabel: I18nStringKey.kAccountModal_NewPassword,
accountSettingsConfirmNewPasswordLabel: I18nStringKey.kAccountModal_ConfirmNewPassword,
accountSettingsCurrentPasswordLabel: I18nStringKey.kAccountModal_CurrentPassword,
updateAccountSettingsBtn: I18nStringKey.kGeneric_Update,
accountResetPasswordEmailLabel: I18nStringKey.kGeneric_EMail,
accountResetPasswordUsernameLabel: I18nStringKey.kGeneric_Username,
accountResetPasswordBtn: I18nStringKey.kAccountModal_ResetPassword,
accountResetPasswordCodeLabel: I18nStringKey.kGeneric_VerificationCode,
accountResetPasswordNewPasswordLabel: I18nStringKey.kAccountModal_NewPassword,
accountResetPasswordConfirmNewPasswordLabel: I18nStringKey.kAccountModal_ConfirmNewPassword,
accountResetPasswordVerifyBtn: I18nStringKey.kAccountModal_ResetPassword,
};
const kDomAttributeToStringMap = {
adminPassword: {
placeholder: I18nStringKey.kGeneric_Password,
},
accountLoginUsername: {
placeholder: I18nStringKey.kGeneric_Username,
},
accountLoginPassword: {
placeholder: I18nStringKey.kGeneric_Password,
},
accountRegisterEmail: {
placeholder: I18nStringKey.kGeneric_EMail,
},
accountRegisterUsername: {
placeholder: I18nStringKey.kGeneric_Username,
},
accountRegisterPassword: {
placeholder: I18nStringKey.kGeneric_Password,
},
accountRegisterConfirmPassword: {
placeholder: I18nStringKey.kAccountModal_ConfirmPassword,
},
accountVerifyEmailCode: {
placeholder: I18nStringKey.kGeneric_VerificationCode,
},
accountVerifyEmailPassword: {
placeholder: I18nStringKey.kGeneric_Password,
},
accountSettingsEmail: {
placeholder: I18nStringKey.kGeneric_EMail,
},
accountSettingsUsername: {
placeholder: I18nStringKey.kGeneric_Username,
},
accountSettingsNewPassword: {
placeholder: I18nStringKey.kAccountModal_NewPassword,
},
accountSettingsConfirmNewPassword: {
placeholder: I18nStringKey.kAccountModal_ConfirmNewPassword,
},
accountSettingsCurrentPassword: {
placeholder: I18nStringKey.kAccountModal_CurrentPassword,
},
accountResetPasswordEmail: {
placeholder: I18nStringKey.kGeneric_EMail,
},
accountResetPasswordUsername: {
placeholder: I18nStringKey.kGeneric_Username,
},
accountResetPasswordCode: {
placeholder: I18nStringKey.kGeneric_VerificationCode,
},
accountResetPasswordNewPassword: {
placeholder: I18nStringKey.kAccountModal_NewPassword,
},
accountResetPasswordConfirmNewPassword: {
placeholder: I18nStringKey.kAccountModal_ConfirmNewPassword,
},
};
for (let domId of Object.keys(kDomIdtoStringMap)) {
let element = document.getElementById(domId);
if (element == null) {
alert('Uhh!! THIS SHOULD NOT BE SEEN!! IF YOU DO YELL LOUDLY');
alert(`Error: Could not find element with ID ${domId} in the DOM! Please tell a site admin this happened.`);
return;
}
@ -245,7 +349,22 @@ export class I18n {
// N.B: For now, we assume all strings in this map are not formatted.
// If this assumption changes, then we should just use GetString() again
// and maybe include arguments, but for now this is okay
element.innerText = this.GetStringRaw(kDomIdtoStringMap[domId]);
element.innerHTML = this.GetStringRaw(kDomIdtoStringMap[domId]);
}
for (let domId of Object.keys(kDomAttributeToStringMap)) {
let element = document.getElementById(domId);
if (element == null) {
alert(`Error: Could not find element with ID ${domId} in the DOM! Please tell a site admin this happened.`);
return;
}
// TODO: Figure out if we can get rid of this ts-ignore
// @ts-ignore
let attributes = kDomAttributeToStringMap[domId];
for (let attr of Object.keys(attributes)) {
element.setAttribute(attr, this.GetStringRaw(attributes[attr] as I18nStringKey));
}
}
}
@ -271,4 +390,4 @@ export class I18n {
}
}
export let TheI18n = new I18n();
export let TheI18n = new I18n();

View File

@ -826,21 +826,21 @@ function userModOptions(user: { user: User; element: HTMLTableRowElement }) {
let ul = document.createElement('ul');
ul.classList.add('dropdown-menu', 'dropdown-menu-dark', 'table-dark', 'text-light');
if (perms.bypassturn) addUserDropdownItem(ul, TheI18n.GetString(I18nStringKey.kVMButtons_EndTurn), () => VM!.endTurn(user.user.username));
if (perms.ban) addUserDropdownItem(ul, 'Ban', () => VM!.ban(user.user.username));
if (perms.kick) addUserDropdownItem(ul, 'Kick', () => VM!.kick(user.user.username));
if (perms.ban) addUserDropdownItem(ul, TheI18n.GetString(I18nStringKey.kAdminVMButtons_Ban), () => VM!.ban(user.user.username));
if (perms.kick) addUserDropdownItem(ul, TheI18n.GetString(I18nStringKey.kAdminVMButtons_Kick), () => VM!.kick(user.user.username));
if (perms.rename)
addUserDropdownItem(ul, 'Rename', () => {
let newname = prompt(`Enter new username for ${user.user.username}`);
addUserDropdownItem(ul, TheI18n.GetString(I18nStringKey.kVMButtons_ChangeUsername), () => {
let newname = prompt(TheI18n.GetString(I18nStringKey.kVMPrompts_AdminChangeUsernamePrompt, user.user.username));
if (!newname) return;
VM!.renameUser(user.user.username, newname);
});
if (perms.mute) {
addUserDropdownItem(ul, 'Temporary Mute', () => VM!.mute(user.user.username, MuteState.Temp));
addUserDropdownItem(ul, 'Indefinite Mute', () => VM!.mute(user.user.username, MuteState.Perma));
addUserDropdownItem(ul, 'Unmute', () => VM!.mute(user.user.username, MuteState.Unmuted));
addUserDropdownItem(ul, TheI18n.GetString(I18nStringKey.kAdminVMButtons_TempMute), () => VM!.mute(user.user.username, MuteState.Temp));
addUserDropdownItem(ul, TheI18n.GetString(I18nStringKey.kAdminVMButtons_IndefMute), () => VM!.mute(user.user.username, MuteState.Perma));
addUserDropdownItem(ul, TheI18n.GetString(I18nStringKey.kAdminVMButtons_Unmute), () => VM!.mute(user.user.username, MuteState.Unmuted));
}
if (perms.grabip)
addUserDropdownItem(ul, 'Get IP', async () => {
addUserDropdownItem(ul, TheI18n.GetString(I18nStringKey.kAdminVMButtons_GetIP), async () => {
let ip = await VM!.getip(user.user.username);
alert(ip);
});
@ -859,7 +859,7 @@ function addUserDropdownItem(ul: HTMLUListElement, text: string, func: () => voi
}
// Admin buttons
elements.restoreBtn.addEventListener('click', () => window.confirm('Are you sure you want to restore the VM?') && VM?.restore());
elements.restoreBtn.addEventListener('click', () => window.confirm(TheI18n.GetString(I18nStringKey.kVMPrompts_AdminRestoreVMPrompt)) && VM?.restore());
elements.rebootBtn.addEventListener('click', () => VM?.reboot());
elements.clearQueueBtn.addEventListener('click', () => VM?.clearQueue());
elements.bypassTurnBtn.addEventListener('click', () => VM?.bypassTurn());
@ -929,7 +929,7 @@ const accountModal = new bootstrap.Modal(elements.accountModal);
elements.accountModalErrorDismiss.addEventListener('click', () => elements.accountModalError.style.display = "none");
elements.accountModalSuccessDismiss.addEventListener('click', () => elements.accountModalSuccess.style.display = "none");
elements.accountLoginButton.addEventListener("click", () => {
elements.accountModalTitle.innerText = TheI18n.GetString(I18nStringKey.kAccountModal_Login);
elements.accountModalTitle.innerText = TheI18n.GetString(I18nStringKey.kGeneric_Login);
elements.accountRegisterSection.style.display = "none";
elements.accountVerifyEmailSection.style.display = "none";
elements.accountLoginSection.style.display = "block";
@ -939,7 +939,7 @@ elements.accountLoginButton.addEventListener("click", () => {
accountModal.show();
});
elements.accountRegisterButton.addEventListener("click", () => {
elements.accountModalTitle.innerText = TheI18n.GetString(I18nStringKey.kAccountModal_Register);
elements.accountModalTitle.innerText = TheI18n.GetString(I18nStringKey.kGeneric_Register);
elements.accountRegisterSection.style.display = "block";
elements.accountVerifyEmailSection.style.display = "none";
elements.accountLoginSection.style.display = "none";

View File

@ -10,6 +10,21 @@
"kGeneric_No": "No",
"kGeneric_Ok": "OK",
"kGeneric_Cancel": "Cancel",
"kGeneric_Send": "Send",
"kGeneric_Understood": "Understood",
"kGeneric_Username": "Username",
"kGeneric_Password": "Password",
"kGeneric_Login": "Log in",
"kGeneric_Register": "Register",
"kGeneric_EMail": "E-Mail",
"kGeneric_DateOfBirth": "Date of Birth",
"kGeneric_VerificationCode": "Verification Code",
"kGeneric_Verify": "Verify",
"kGeneric_Update": "Update",
"kGeneric_Logout": "Log out",
"kWelcomeModal_Header": "Welcome to CollabVM",
"kWelcomeModal_Body": "<p>Before continuing, please familiarize yourself with our rules:</p> <h3>R1. Don't break the law.</h3> Do not use CollabVM or CollabVM's network to violate United States federal law, New York state law, or international law. If CollabVM becomes aware a crime has been committed through its service, you will be immediately banned, and your activities may be reported to the authorities if necessary.<br><br>CollabVM is required by law to notify law enforcement agencies if it becomes aware of the presence of child pornography on, or being transmitted through its network.<br><br>COPPA is also enforced, please do not use CollabVM if you are under the age of 13 years old. <h3>R2. No running DoS/DDoS tools.</h3> Do not use CollabVM to DoS/DDoS an indivdiual, business, company, or anyone else. <h3>R3. No spam distribution.</h3> Do not spam any emails using this service or push spam in general. <h3>R4. Do not abuse any exploits.</h3> Do not abuse any exploits, additionally if you see someone abusing exploits or you need to report one, please contact me at: computernewbab@gmail.com <h3>R5. Don't impersonate other users.</h3> Do not impersonate other members of CollabVM. If caught, you'll be temporarily disconnected, and banned if necessary. <h3>R6. One vote per person.</h3> Do not use any methods or tools to bypass the vote restriction. Only one vote per person is allowed, no matter what. Anybody who is caught doing this will be banned. <h3>R7. No Remote Administration Tools.</h3> Do not use any remote administration tools (ex: DarkComet, NanoCore, Anydesk, TeamViewer, Orcus, etc.) <h3>R8. No bypassing CollabNet.</h3> Do not attempt to bypass the blocking provided by CollabNet, especially if it is being used to break Rule 1, Rule 2, or Rule 7 (or run stupid over-used things). <h3>R9. No performing destructive actions constantly.</h3> Any user may not destroy the VM (rendering it unusable constantly), install/reinstall the operating system (except on VM7 or VM8), or run bots that do such. This includes bots that spam massive amounts of keyboard/mouse input (\"kitting\"). <h3>R10. No Cryptomining</h3> Attempting to mine cryptocurrency on the VMs will result in a kick, and then a permanent ban if you keep attempting. Besides, it's not like you're gonna make any money off it. <h3>NSFW Warning</h3> Please note that NSFW content is allowed on our anarchy VM (VM0b0t), and is viewed regularly. In addition, while we give a good effort to keep NSFW off the main VMs, people will occasionally slip it through.",
"kSiteButtons_Home": "Home",
"kSiteButtons_FAQ": "FAQ",
@ -29,26 +44,48 @@
"kVMButtons_TakeTurn": "Take Turn",
"kVMButtons_EndTurn": "End Turn",
"kVMButtons_ChangeUsername": "Change Username",
"kVMButtons_Keyboard": "Keyboard",
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
"kVMButtons_VoteForReset": "Vote For Reset",
"kVMButtons_Screenshot": "Screenshot",
"kQEMUMonitor": "QEMU Monitor",
"kAdminVMButtons_PassVote": "Pass Vote",
"kAdminVMButtons_CancelVote": "Cancel Vote",
"kAdminVMButtons_Restore": "Restore",
"kAdminVMButtons_Reboot": "Reboot",
"kAdminVMButtons_ClearTurnQueue": "Clear Turn Queue",
"kAdminVMButtons_BypassTurn": "Bypass Turn",
"kAdminVMButtons_IndefiniteTurn": "Indefinite Turn",
"kAdminVMButtons_Ban": "Ban",
"kAdminVMButtons_Kick": "Kick",
"kAdminVMButtons_TempMute": "Temporary Mute",
"kAdminVMButtons_IndefMute": "Indefinite Mute",
"kAdminVMButtons_Unmute": "Unmute",
"kAdminVMButtons_GetIP": "Get IP",
"kVMPrompts_AdminChangeUsernamePrompt": "Enter new username for {0}:",
"kVMPrompts_AdminRestoreVMPrompt": "Are you sure you want to restore the VM?",
"kVMPrompts_EnterNewUsernamePrompt": "Enter a new username, or leave the field blank to be assigned a guest username",
"kError_UnexpectedDisconnection": "You have been disconnected from the server.",
"kError_UsernameTaken": "That username is already taken",
"kError_UsernameInvalid": "Usernames can contain only numbers, letters, spaces, dashes, underscores, and dots, and it must be between 3 and 20 characters.",
"kError_UsernameBlacklisted": "That username has been blacklisted.",
"kError_IncorrectPassword": "Incorrect password.",
"kAccountModal_Login": "Login",
"kAccountModal_Register": "Register",
"kAccountModal_Verify": "Verify E-Mail",
"kAccountModal_AccountSettings": "Account Settings",
"kAccountModal_ResetPassword": "Reset Password",
"kAccountModal_NewPassword": "New Password",
"kAccountModal_ConfirmNewPassword": "Confirm New Password",
"kAccountModal_CurrentPassword": "Current Password",
"kAccountModal_ConfirmPassword": "Confirm Password",
"kMissingCaptcha": "Please fill out the captcha.",
"kPasswordsMustMatch": "Passwords must match.",
"kAccountModal_VerifyText": "We sent an E-Mail to {0}. To verify your account, please enter the 8-digit code from the E-Mail below.",