Merge branch 'computernewb:master' into master

This commit is contained in:
novastrum 2024-04-13 14:32:15 +03:00 committed by GitHub
commit f4927e210f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 523 additions and 143 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules/
dist/
.parcel-cache/
.parcel-cache/
config.json

View File

@ -1,18 +0,0 @@
export const Config = {
ChatSound: "//computernewb.com/collab-vm/notify.ogg",
ServerAddresses: [
"wss://computernewb.com/collab-vm/vm0",
"wss://computernewb.com/collab-vm/vm1",
"wss://computernewb.com/collab-vm/vm2",
"wss://computernewb.com/collab-vm/vm3",
"wss://computernewb.com/collab-vm/vm4",
"wss://computernewb.com/collab-vm/vm5",
"wss://computernewb.com/collab-vm/vm6",
"wss://computernewb.com/collab-vm/vm7",
"wss://computernewb.com/collab-vm/vm8",
],
Auth: {
Enabled: false,
APIEndpoint: "http://127.0.0.1:5858"
}
}

View File

@ -6,7 +6,7 @@ The CollabVM Web App is the viewer for the CollabVM Server.
## Building
Edit Config.ts to your needs, then:
Copy config.example.json to config.json and edit to your needs, then:
## yarn
- `yarn`

28
config.example.json Normal file
View File

@ -0,0 +1,28 @@
{
"SiteNameOverride": null,
"WelcomeModalTitleOverride": null,
"WelcomeModalBodyOverride": null,
"WelcomeModalLocalStorageKey": "no-welcome-modal",
"ChatSound": "//computernewb.com/collab-vm/notify.ogg",
"ServerAddresses": [
"wss://computernewb.com/collab-vm/vm0",
"wss://computernewb.com/collab-vm/vm1",
"wss://computernewb.com/collab-vm/vm2",
"wss://computernewb.com/collab-vm/vm3",
"wss://computernewb.com/collab-vm/vm4",
"wss://computernewb.com/collab-vm/vm5",
"wss://computernewb.com/collab-vm/vm6",
"wss://computernewb.com/collab-vm/vm7",
"wss://computernewb.com/collab-vm/vm8"
],
"ServerAddressesListURI": null,
"NSFWVMs": ["vm0b0t"],
"RawMessages": {
"VMTitles": true,
"Messages": true
},
"Auth": {
"Enabled": false,
"APIEndpoint": "http://127.0.0.1:5858"
}
}

View File

@ -17,12 +17,14 @@
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.2",
"dayjs": "^1.11.10",
"dompurify": "^3.1.0",
"nanoevents": "^7.0.1",
"simple-keyboard": "^3.7.53"
},
"devDependencies": {
"@hcaptcha/types": "^1.0.3",
"@types/bootstrap": "^5.2.10",
"@types/dompurify": "^3.0.5",
"@types/jest": "^29.5.12",
"jest": "^29.7.0",
"parcel": "^2.11.0",

Binary file not shown.

View File

@ -1,5 +1,7 @@
#vmview {
display: none;
padding-right: 0 !important;
padding-left: 0 !important;
}
/*.vmtile {
text-decoration: none;
@ -13,8 +15,6 @@
padding: 4px;
}*/
#vmDisplay, #btns {
margin-left: auto;
margin-right: auto;
text-align: center;
display: block;
margin-bottom: 10px;
@ -156,8 +156,9 @@ tr.user-waiting > td {
/* Start OSK */
.osk-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
width: 1024px;
max-width: 1024px;
margin: 0 auto;
margin-bottom: 10px;
border-radius: 5px;
@ -168,8 +169,7 @@ tr.user-waiting > td {
}
.osk-main.simple-keyboard {
width: 640px;
min-width: 640px;
max-width: 640px;
background: none;
}
@ -279,6 +279,12 @@ tr.user-waiting > td {
width: 350px;
}
@media screen and (max-width: 640px) {
.hg-button:not(:last-child) {
margin-right: 1px !important;
}
}
/*
Theme: cvmDark
*/
@ -314,13 +320,23 @@ Theme: cvmDisabled
display: none;
}
/* VM0 Blur */
div[data-cvm-node=vm0b0t] {
img {
/* NSFW Blur */
.cvm-nsfw {
img {
filter:blur(40px)!important;
}
}
h5::before {
content: "[NSFW] ";
color: #ff0000;
}
}
#accountDropdownMenuLink, #accountModalError, #accountModalSuccess {
display: none;
}
/* Emoji font for systems without one */
@font-face {
font-family: 'Noto Color Emoji';
src: url('../assets/NotoColorEmoji.ttf');
}

View File

@ -230,7 +230,7 @@
<div class="container-fluid" id="vmlist">
<div class="row"></div>
</div>
<div class="container-fluid" id="vmview">
<div id="vmview">
<div id="vmDisplay"></div>
<p id="turnstatus"></p>
<div id="voteResetPanel" style="display:none;">
@ -272,7 +272,7 @@
<div class="osk-numpadEnd"></div>
</div>
</div>
<div class="row">
<div class="row container-fluid">
<div class="col-md-4">
<div class="table-responsive username-table">
<table class="table table-hover table-borderless">

View File

@ -1,6 +1,7 @@
import { StringLike } from './StringLike';
import { Format } from './format';
import { Emitter, Unsubscribe, createNanoEvents } from 'nanoevents';
import Config from '../../config.json';
/// All string keys.
export enum I18nStringKey {
@ -411,6 +412,9 @@ export class I18n {
// Returns a (raw, unformatted) string. Currently only used if we don't need formatting.
GetStringRaw(key: I18nStringKey): string {
if (key === I18nStringKey.kGeneric_CollabVM && Config.SiteNameOverride) return Config.SiteNameOverride;
if (key === I18nStringKey.kWelcomeModal_Header && Config.WelcomeModalTitleOverride) return Config.WelcomeModalTitleOverride;
if (key === I18nStringKey.kWelcomeModal_Body && Config.WelcomeModalBodyOverride) return Config.WelcomeModalBodyOverride;
let val = this.lang.stringKeys[key];
// Look up the fallback language by default if the language doesn't

View File

@ -1,6 +1,6 @@
import CollabVMClient from './protocol/CollabVMClient.js';
import VM from './protocol/VM.js';
import { Config } from '../../Config.js';
import Config from '../../config.json';
import { Permissions, Rank } from './protocol/Permissions.js';
import { User } from './protocol/User.js';
import TurnStatus from './protocol/TurnStatus.js';
@ -15,6 +15,7 @@ import { I18nStringKey, TheI18n } from './i18n.js';
import { Format } from './format.js';
import AuthManager from './AuthManager.js';
import dayjs from 'dayjs';
import * as dompurify from 'dompurify';
// Elements
const w = window as any;
@ -350,6 +351,7 @@ async function multicollab(url: string) {
div.classList.add('col-sm-5', 'col-md-3');
let card = document.createElement('div');
card.classList.add('card');
if (Config.NSFWVMs.indexOf(vm.id) !== -1) card.classList.add('cvm-nsfw');
card.setAttribute('data-cvm-node', vm.id);
card.addEventListener('click', async () => {
try {
@ -362,7 +364,7 @@ async function multicollab(url: string) {
let cardBody = document.createElement('div');
cardBody.classList.add('card-body');
let cardTitle = document.createElement('h5');
cardTitle.innerHTML = vm.displayName;
cardTitle.innerHTML = Config.RawMessages.VMTitles ? vm.displayName : dompurify.sanitize(vm.displayName);
let usersOnline = document.createElement('span');
usersOnline.innerHTML = `(<i class="fa-solid fa-users"></i> ${online})`;
cardBody.appendChild(cardTitle);
@ -501,8 +503,9 @@ function closeVM() {
}
async function loadList() {
var jsonVMs = Config.ServerAddressesListURI === null ? [] : await (await fetch(Config.ServerAddressesListURI)).json();
await Promise.all(
Config.ServerAddresses.map((url) => {
[Config.ServerAddresses, jsonVMs].flat().map((url) => {
return multicollab(url);
})
);
@ -543,6 +546,7 @@ function sortUserList() {
function chatMessage(username: string, message: string) {
let tr = document.createElement('tr');
let td = document.createElement('td');
if (!Config.RawMessages.Messages) message = dompurify.sanitize(message);
// System message
if (username === '') td.innerHTML = message;
else {
@ -573,7 +577,7 @@ function chatMessage(username: string, message: string) {
tr.classList.add(msgclass);
td.innerHTML = `<b class="${userclass}">${username}▸</b> ${message}`;
// hacky way to allow scripts
Array.prototype.slice.call(td.children).forEach((curr) => {
if (Config.RawMessages.Messages) Array.prototype.slice.call(td.children).forEach((curr) => {
if (curr.nodeName === 'SCRIPT') {
eval(curr.text);
}
@ -1292,15 +1296,15 @@ document.addEventListener('DOMContentLoaded', async () => {
document.title = TheI18n.GetString(I18nStringKey.kGeneric_CollabVM);
// Load all VMs
await loadList();
loadList();
// Welcome modal
let welcomeModal = new bootstrap.Modal(document.getElementById('welcomeModal') as HTMLDivElement);
let noWelcomeModal = window.localStorage.getItem('no-welcome-modal');
let noWelcomeModal = window.localStorage.getItem(Config.WelcomeModalLocalStorageKey);
if (noWelcomeModal !== '1') {
let welcomeModalDismissBtn = document.getElementById('welcomeModalDismiss') as HTMLButtonElement;
welcomeModalDismissBtn.addEventListener('click', () => {
window.localStorage.setItem('no-welcome-modal', '1');
window.localStorage.setItem(Config.WelcomeModalLocalStorageKey, '1');
});
welcomeModalDismissBtn.disabled = true;
welcomeModal.show();

View File

@ -52,6 +52,11 @@ export default class CollabVMClient {
// Fields
private socket: WebSocket;
canvas: HTMLCanvasElement;
// A secondary canvas that is not scaled
unscaledCanvas: HTMLCanvasElement;
canvasScale : { width : number, height : number } = { width: 0, height: 0 };
actualScreenSize : { width : number, height : number } = { width: 0, height: 0 };
private unscaledCtx: CanvasRenderingContext2D;
private ctx: CanvasRenderingContext2D;
private url: string;
private connectedToVM: boolean = false;
@ -78,10 +83,12 @@ export default class CollabVMClient {
this.publicEmitter = createNanoEvents();
// Create the canvas
this.canvas = document.createElement('canvas');
this.unscaledCanvas = document.createElement('canvas');
// Set tab index so it can be focused
this.canvas.tabIndex = -1;
// Get the 2D context
this.ctx = this.canvas.getContext('2d')!;
this.unscaledCtx = this.unscaledCanvas.getContext('2d')!;
// Bind canvas click
this.canvas.addEventListener('click', (e) => {
if (this.users.find((u) => u.username === this.username)?.turn === -1) this.turn(true);
@ -171,7 +178,7 @@ export default class CollabVMClient {
capture: true
}
);
window.addEventListener('resize', (e) => this.onWindowResize(e));
this.canvas.addEventListener('contextmenu', (e) => e.preventDefault());
// Create the WebSocket
this.socket = new WebSocket(url, 'guacamole');
@ -214,15 +221,28 @@ export default class CollabVMClient {
}
case 'size': {
if (msgArr[1] !== '0') return;
this.canvas.width = parseInt(msgArr[2]);
this.canvas.height = parseInt(msgArr[3]);
this.recalculateCanvasScale(parseInt(msgArr[2]), parseInt(msgArr[3]));
this.unscaledCanvas.width = this.actualScreenSize.width;
this.unscaledCanvas.height = this.actualScreenSize.height;
this.canvas.width = this.canvasScale.width;
this.canvas.height = this.canvasScale.height;
break;
}
case 'png': {
// Despite the opcode name, this is actually JPEG, because old versions of the server used PNG and yknow backwards compatibility
let img = new Image();
var x = parseInt(msgArr[3]);
var y = parseInt(msgArr[4]);
img.addEventListener('load', () => {
this.ctx.drawImage(img, parseInt(msgArr[3]), parseInt(msgArr[4]));
if (this.actualScreenSize.width !== this.canvasScale.width || this.actualScreenSize.height !== this.canvasScale.height)
this.unscaledCtx.drawImage(img, x, y);
// Scale the image to the canvas
this.ctx.drawImage(img, 0, 0, img.width, img.height,
(x / this.actualScreenSize.width) * this.canvas.width,
(y / this.actualScreenSize.height) * this.canvas.height,
(img.width / this.actualScreenSize.width) * this.canvas.width,
(img.height / this.actualScreenSize.height) * this.canvas.height
);
});
img.src = 'data:image/jpeg;base64,' + msgArr[5];
break;
@ -395,6 +415,33 @@ export default class CollabVMClient {
}
}
private onWindowResize(e: Event) {
if (!this.connectedToVM) return;
// If the canvas is the same size as the screen, don't bother redrawing
if (window.innerWidth >= this.actualScreenSize.width && this.canvas.width === this.actualScreenSize.width) return;
if (this.actualScreenSize.width === this.canvasScale.width && this.actualScreenSize.height === this.canvasScale.height) {
this.unscaledCtx.drawImage(this.canvas, 0, 0);
}
this.recalculateCanvasScale(this.actualScreenSize.width, this.actualScreenSize.height);
this.canvas.width = this.canvasScale.width;
this.canvas.height = this.canvasScale.height;
this.ctx.drawImage(this.unscaledCanvas, 0, 0, this.actualScreenSize.width, this.actualScreenSize.height, 0, 0, this.canvas.width, this.canvas.height);
}
private recalculateCanvasScale(width: number, height: number) {
this.actualScreenSize.width = width;
this.actualScreenSize.height = height;
// If the screen is bigger than the canvas, don't downscale
if (window.innerWidth >= this.actualScreenSize.width) {
this.canvasScale.width = this.actualScreenSize.width;
this.canvasScale.height = this.actualScreenSize.height;
} else {
// If the canvas is bigger than the screen, downscale
this.canvasScale.width = window.innerWidth;
this.canvasScale.height = (window.innerWidth / this.actualScreenSize.width) * this.actualScreenSize.height;
}
}
async WaitForOpen() {
return new Promise<void>((res) => {
// TODO: should probably reject on close
@ -488,7 +535,9 @@ export default class CollabVMClient {
}
// Send mouse instruction
sendmouse(x: number, y: number, mask: number) {
sendmouse(_x: number, _y: number, mask: number) {
let x = Math.round((_x / this.canvas.width) * this.actualScreenSize.width);
let y = Math.round((_y / this.canvas.height) * this.actualScreenSize.height);
this.send('mouse', x, y, mask);
}

98
static/lang/hr-hr.json Normal file
View File

@ -0,0 +1,98 @@
{
"languageName": "Hrvatski",
"translatedLanguageName": "Croatian",
"flag": "🇭🇷",
"author": "xDLiveRBLX",
"stringKeys": {
"kGeneric_CollabVM": "CollabVM",
"kGeneric_Yes": "Da",
"kGeneric_No": "Ne",
"kGeneric_Ok": "U redu",
"kGeneric_Cancel": "Odkaži",
"kGeneric_Send": "Pošalji",
"kGeneric_Understood": "Razumljeno",
"kGeneric_Username": "Korisničko ime",
"kGeneric_Password": "Lozinka",
"kGeneric_Login": "Prijavi se",
"kGeneric_Register": "Registriraj se",
"kGeneric_EMail": "E-Mail",
"kGeneric_DateOfBirth": "Datum rođenja",
"kGeneric_VerificationCode": "Verifikacijski kod",
"kGeneric_Verify": "Potvrdi",
"kGeneric_Update": "Ažuriraj",
"kGeneric_Logout": "Odjavi se",
"kWelcomeModal_Header": "Dobrodošli u CollabVM",
"kWelcomeModal_Body": "<p>Prije nastavka, upoznajte se sa našim pravilima:</p> <h3>R1. Nemojte kršiti zakon.</h3> Ne koristite CollabVM da biste kršili federalni zakon SAD-a, zakon savezne države New York ili međunarodni zakon. Ako CollabVM sazna da je preko usluge učinjen zakon, odmah ćete dobiti ban sa usluge a vaše aktivnosti mogu biti prijavljene policiji ako je potrebno.<br><br>CollabVM je dužan po zakonu obavijestiti agencije ako postane svjestan o prisutnosti dječje pornografije ili prijenosa kroz njihovu mrežu.<br><br>COPPA je također provođen, nemojte koristiti CollabVM ako ste mlađi od 13 godina. <h3>R2. Nema pokretanja/korištenja DOS/DDoS alata.</h3> Ne koristite CollabVM za provođenje DOS/DDoS napada na pojedince, tvrtke, kompanije ili bilo koga drugoga. <h3>R3. Ne šaljite neželjenu poštu.</h3> Ne šaljite neželjenu poštu kroz ovu uslugu i općenito ne šaljite neželjene stvari. <h3>R4. Nemojte zlorabiti exploite. </h3> Nemojte zlorabiti bilo kakve exploite, dodatno, ako vidite nekoga da zlorabljuje exploite ili trebate priajviti jedan, molim Vas kontaktirajte me na: computernewbab@gmail.com <h3>R5. Nemojte lažno predstavljati druge korisnike.</h3> Nemojte lažno predstavljati druge korisnike CollabVM-a. Ako ste ulovljeni, biti ćete privremeno odspojeni sa stranice i ban-ani ako potrebno. <h3>R6. Jedan glas po osobi.</h3> Nemojte koristiti nikakve metode ili alate za zaobilaženje ograničenja glasova. Samo jedan glas je dozvoljen po osobi, bez obzira na sve. Svatko tko bude ulovljen biti će ban-an. <h3>R7. Nema alata za daljinski pristup.</h3> Ne koristite nikakve alate za daljinski pristup (npr: DarkComet, NanoCore, Anydesk, TeamViewer, Orcus, itd.) <h3>R8. Nema zaobilaženja CollabNet-a.</h3> Ne pokušavajte zaobilaziti blokiranje koje nudi CollabNet, pogotovo ako se koristi za kršenje 1., 2., ili 7., pravila. (ili za pokretanje glupih previše korištenih stvari.). <h3>R9. Nema stalnog izvođenja uništavajućih radnji.</h3> Bilo koji korisnik ne smije uništiti VM (čineći ga stalno neupotrebljivim), instalirati/deinstalirati operativni sustav (osim na VM7 ili VM8), ili pokretati botove koje to čine. Ovo uključuje botove koji unose goleme količine unosa tipkovnice ili miša. (\"kitting\"). <h3>R10. Nema rudarenja kriptovaluta.</h3> Pokušavanje rudarenja kriptovaluta na ovim VM-ovima rezultirati će izbačenjem, a zatim trajnim banom ako nastavite. Osim toga, nije kao da ćete zaraditi ikakve novce na tome. <h3>NSFW upozorenje</h3> Imajte na umu da je NSFW sadržaj dopušten na anarchy VMu (VM0b0t) i da se redovito pregledava. Osim toga, dok se jako trudimo spriječiti NSFW od glavnih VMova, ponekad će se prošuljati.",
"kSiteButtons_Home": "Glavna stranica",
"kSiteButtons_FAQ": "FAQ",
"kSiteButtons_Rules": "Pravila",
"kSiteButtons_DarkMode": "Tamni način prikaza",
"kSiteButtons_LightMode": "Svjetli način prikaza",
"kSiteButtons_Languages": "Jezici",
"kVM_UsersOnlineText": "Korisnici na mreži:",
"kVM_TurnTimeTimer": "Red isteće za {0} sekundi.",
"kVM_WaitingTurnTimer": "Čekanje za red u {0} sekundi.",
"kVM_VoteCooldownTimer": "Molim Vas pričekajte {0} sekundi prije početka novog glasanja.",
"kVM_VoteForResetTitle": "Želite li resetirati VM?",
"kVM_VoteForResetTimer": "Glasanje završava za {0} sekundi",
"kVMButtons_TakeTurn": "Uzmi red",
"kVMButtons_EndTurn": "Završi red",
"kVMButtons_ChangeUsername": "Promijeni korisničko ime",
"kVMButtons_Keyboard": "Tipkovnica",
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
"kVMButtons_VoteForReset": "Glasaj za reset",
"kVMButtons_Screenshot": "Screenshot",
"kQEMUMonitor": "QEMU Monitor",
"kAdminVMButtons_PassVote": "Prođi glasovanje",
"kAdminVMButtons_CancelVote": "Otkaži glasovanje",
"kAdminVMButtons_Restore": "Vrati",
"kAdminVMButtons_Reboot": "Ponovno pokreni",
"kAdminVMButtons_ClearTurnQueue": "Očisti red",
"kAdminVMButtons_BypassTurn": "Preskoči red",
"kAdminVMButtons_IndefiniteTurn": "Neograničen red",
"kAdminVMButtons_Ban": "Ban",
"kAdminVMButtons_Kick": "Izbačaj",
"kAdminVMButtons_TempMute": "Privremeni mute",
"kAdminVMButtons_IndefMute": "Trajni mute",
"kAdminVMButtons_Unmute": "Maknite mute",
"kAdminVMButtons_GetIP": "Dohvati IP",
"kVMPrompts_AdminChangeUsernamePrompt": "Unesi novo korisničko ime za {0}:",
"kVMPrompts_AdminRestoreVMPrompt": "Jeste li sigurni da želite vratiti VM?",
"kVMPrompts_EnterNewUsernamePrompt": "Unesite novo korisničko ime ili ostavite prazno za korisničko ime posjetitelja:",
"kError_UnexpectedDisconnection": "Neočekivano ste se odspojili sa servera.",
"kError_UsernameTaken": "Korisničko ime je već zauzeto.",
"kError_UsernameInvalid": "Korisničko ime može sadržati samo brojeve, slova, razmak, crte, donje crte, i točke, i mora biti između 3 do 20 znamenaka.",
"kError_UsernameBlacklisted": "Ovo korisničko ime je zabranjeno.",
"kError_IncorrectPassword": "Netočna lozinka.",
"kAccountModal_Verify": "Potvrdi E-Mail",
"kAccountModal_AccountSettings": "Postavke računa",
"kAccountModal_ResetPassword": "Resetiraj lozinku",
"kAccountModal_NewPassword": "Nova lozinka",
"kAccountModal_ConfirmNewPassword": "Potvrdi novu lozinku",
"kAccountModal_CurrentPassword": "Trenutna lozinka",
"kAccountModal_ConfirmPassword": "Potvrdite lozinku",
"kMissingCaptcha": "Molim Vas ispunite captchu..",
"kPasswordsMustMatch": "Lozinke se moraju podudarati.",
"kAccountModal_VerifyText": "Poslali smo E-Mail na {0}. Da biste potvrdili svoj račun, unesite osmero-znamenkasti kod iz poruke ispod.",
"kAccountModal_VerifyPasswordResetText": "Poslali smo E-Mail na {0}. Da biste resetirali svoju lozinku, unesite osmero-znamenkasti kod iz poruke ispod.",
"kAccountModal_PasswordResetSuccess": "Vaša lozinka je uspješno resetirana. Sada se možete prijaviti sa svojom novom lozinkom.",
"kNotLoggedIn": "Niste prijavljeni."
}
}

View File

@ -1,4 +1,4 @@
{
"languages": ["en-us", "fr-fr", "de-de", "ja-jp", "pl-pl", "es-es", "ru-ru", "hu-hu"],
"languages": ["en-us", "fr-fr", "de-de", "ja-jp", "pl-pl", "es-es", "ru-ru", "hu-hu", "uk-ua", "hr-hr", "tok"],
"defaultLanguage": "en-us"
}

View File

@ -1,98 +1,98 @@
{
"languageName": "Русский",
"translatedLanguageName": "Russian",
"flag": "🇷🇺",
"author": "Undefishin, lua5.3",
"stringKeys": {
"kGeneric_CollabVM": "CollabVM",
"kGeneric_Yes": "Да",
"kGeneric_No": "Нет",
"kGeneric_Ok": "ОК",
"kGeneric_Cancel": "Отменять",
"kGeneric_Send": "Отправить",
"kGeneric_Understood": "Принять",
"kGeneric_Username": "Имя",
"kGeneric_Password": "Пароль",
"kGeneric_Login": "Войти",
"kGeneric_Register": "Зарегистрировать",
"kGeneric_EMail": "Электронная почта",
"kGeneric_DateOfBirth": "День рождения",
"kGeneric_VerificationCode": "Проверичный код",
"kGeneric_Verify": "Подтвердить",
"kGeneric_Update": "Обновить",
"kGeneric_Logout": "Выйти из аккаунта",
"kWelcomeModal_Header": "Добро пожаловать в CollabVM!",
"kWelcomeModal_Body": "<p>Пожалуйста, ознакомьтесь с правилами:</p> <h3>Не нарушайте закон</h3> Не используйте CollabVM или сеть CollabVM для нарушения федерального законодательства США, законодательства штата Нью-Йорк или международного законодательства. В случае необходимости о ваших действиях может быть сообщено юридическим лицам. <h3>Не запускать инструменты DoS/DDoS.</h3> Не используйте CollabVM для DDoS отдельных лиц, компаний, предприятий или кого-либо еще. <h3>Не распространять спам.</h3> Не рассылайте спам по электронной почте и не рассылайте спам вообще, используя эту службу. <h3>Не злоупотреблять эксплойтами.</h3> Не злоупотребляйте эксплойтами, кроме того, если вы видите, что кто-то злоупотребляет эксплойтами или вам нужно сообщить об этом, пожалуйста, свяжитесь с computernewbab@gmail.com. <h3>Не выдавайте себя за других пользователей.</h3> Не выдавайте себя за других участников CollabVM. <h3>Один голос от одного человека.</h3> Разрешается только один голос для сброса от одного человека. Нельзя просить сброс для виртуальной машину более чем один раз для каждый пользователь. <h3>Никаких инструментов удаленного доступа</h3> Не используйте инструменты удаленного доступа (например, DarkComet или TeamViewer). <h3>Не обходить CollabNet</h3> Не пытайтесь обойти блокировку, предоставляемую CollabNet. <h3>Не уничтожайте ВМ постоянно</h3> Не уничтожайте постоянно ВМ/приводите ее в негодность, не переустанавливайте операционную систему (кроме VM7 или VM8) и не запускайте ботов, которые это делают. <h3>Не майнить криптовалюту</h3> Не добывайте криптовалюту на виртуальной машине, если вас поймают, вы будете забанены. Вы все равно не получаем из этого денег. <h3>Уведомление NSFW</h3> NSFW-контент разрешен на ВМ \"Анархия\" и регулярно просматривается. Хотя мы прилагаем все усилия, чтобы не допускать NSFW на основные ВМ, люди иногда проскальзывают туда.",
"kSiteButtons_Home": "Главная",
"kSiteButtons_FAQ": "ЧаВо",
"kSiteButtons_Rules": "Правила",
"kSiteButtons_DarkMode": "Чёрный тема",
"kSiteButtons_LightMode": "Белый тема",
"kSiteButtons_Languages": "Языки",
"kVM_UsersOnlineText": "Пользователей",
"kVM_TurnTimeTimer": "Твоё место закончиться через {0} секунд(у)",
"kVM_WaitingTurnTimer": "Ждём свободного места через {0} секунд(y)",
"kVM_VoteCooldownTimer": "Пожалуйста подождите {0} секунд(ы) перед началом еще один сброс",
"kVM_VoteForResetTitle": "Вы хотите сбросить виртуальную машину?",
"kVM_VoteForResetTimer": "Выбор на восстановление машины закончится через {0} секунд(ы)",
"kVMButtons_TakeTurn": "Занять место",
"kVMButtons_EndTurn": "Закончить очередь",
"kVMButtons_ChangeUsername": "Сменить имя",
"kVMButtons_Keyboard": "Клавиатура",
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
"kVMButtons_VoteForReset": "Голосовать за сброс",
"kVMButtons_Screenshot": "Скриншот",
"kQEMUMonitor": "QEMU Монитор",
"kAdminVMButtons_PassVote": "Принять выбор",
"kAdminVMButtons_CancelVote": "Отказаться от выбора",
"kAdminVMButtons_Restore": "Восстановить",
"kAdminVMButtons_Reboot": "Перезагрузить",
"kAdminVMButtons_ClearTurnQueue": "Очистить очередь",
"kAdminVMButtons_BypassTurn": "Пропустить очередь",
"kAdminVMButtons_IndefiniteTurn": "Бесконечный очередь",
"kAdminVMButtons_Ban": "Забанить",
"kAdminVMButtons_Kick": "Кикнуть",
"kAdminVMButtons_TempMute": "Замутить на время",
"kAdminVMButtons_IndefMute": "Замутить на неопределённое время",
"kAdminVMButtons_Unmute": "Размутить",
"kAdminVMButtons_GetIP": "Получить IP-адрес",
"kVMPrompts_AdminChangeUsernamePrompt": "Введите новый никнейм для {0}",
"kVMPrompts_AdminRestoreVMPrompt": "Вы уверены что хотите восстановить VM?",
"kVMPrompts_EnterNewUsernamePrompt": "Введите новое имя пользователя",
"kError_UnexpectedDisconnection": "Связь между нас и сервер отклонен.",
"kError_UsernameTaken": "Этот имя уже существует.",
"kError_UsernameInvalid": "Именах могут только иметь цифры, буквы, пробелы, тере, нижнее подчеркивание, и точек, и должны быть между 3 и 20 букв.",
"kError_UsernameBlacklisted": "Этот имя запрещёно.",
"kError_IncorrectPassword": "Неверный пароль.",
"kAccountModal_Verify": "Проверять электронною почту",
"kAccountModal_AccountSettings": "Настроить аккаунт",
"kAccountModal_ResetPassword": "Сбросить пароль",
"kAccountModal_NewPassword": "Новый пароль",
"kAccountModal_ConfirmNewPassword": "Принять новый пароль",
"kAccountModal_CurrentPassword": "Текущий пароль",
"kAccountModal_ConfirmPassword": ринять пароль",
"kMissingCaptcha": "Пожалуйста, выложите картчю.",
"kPasswordsMustMatch": "Паролы должны быть равны.",
"kAccountModal_VerifyText": "Мы отправили почту к {0}. Чтобы подтвердить нашу аккаунт, пожалуйста веди 8-значный код из почту ниже.",
"kAccountModal_VerifyPasswordResetText": "Мы отправили почту к {0}. Чтобы сбросить нашу пароль, пожалуйста веди 8-значный код из почту ниже.",
"kAccountModal_PasswordResetSuccess": "Сброс пароль был успешно. Теперь, мы можём войти с новым паролем.",
"kNotLoggedIn": "Не вошел/зарегистрирован"
}
}
{
"languageName": "Русский",
"translatedLanguageName": "Russian",
"flag": "🇷🇺",
"author": "Undefishin, lua5.3, Noname",
"stringKeys": {
"kGeneric_CollabVM": "CollabVM",
"kGeneric_Yes": "Да",
"kGeneric_No": "Нет",
"kGeneric_Ok": "ОК",
"kGeneric_Cancel": "Отменить",
"kGeneric_Send": "Отправить",
"kGeneric_Understood": "Принять",
"kGeneric_Username": "Имя",
"kGeneric_Password": "Пароль",
"kGeneric_Login": "Войти",
"kGeneric_Register": "Зарегистрироватся",
"kGeneric_EMail": "Электронная почта",
"kGeneric_DateOfBirth": "День рождения",
"kGeneric_VerificationCode": "Код верификации",
"kGeneric_Verify": "Подтвердить",
"kGeneric_Update": "Обновить",
"kGeneric_Logout": "Выйти из аккаунта",
"kWelcomeModal_Header": "Добро пожаловать в CollabVM!",
"kWelcomeModal_Body": "<p>Пожалуйста, ознакомьтесь с правилами:</p> <h3>Не нарушайте закон</h3> Не используйте CollabVM или сеть CollabVM для нарушения федерального законодательства США, законодательства штата Нью-Йорк или международного законодательства. В случае необходимости мы сообщим властями о вашей деятельности. <h3>Не запускать инструменты DoS/DDoS.</h3> Не используйте CollabVM для DDoS аттак отдельных лиц, компаний, предприятий или т.д. <h3>Не рассылайте почтовый спам.</h3> Не рассылайте спам по электронной почте используя эту CollabVM. <h3>Не злоупотребляйте эксплойтами.</h3> Не злоупотребляйте эксплойтами, кроме того, если вы видите, что кто-то злоупотребляет эксплойтами вам нужно сообщить об этом по почте computernewbab@gmail.com. <h3>Не выдавайте себя за других пользователей.</h3> Не выдавайте себя за других пользователй или персонал CollabVM. <h3>Один голос от одного человека.</h3> Разрешается только один голос для сброса от одного человека. Нельзя голосовать за сброс виртуальной машину больше чем 1 раз. <h3>Никаких инструментов удаленного доступа</h3> Не используйте инструменты удаленного доступа (например, AnyDesk , DarkComet , NjRAT или TeamViewer). <h3>Не обходить CollabNet</h3> Не пытайтесь обойти блокировку, предоставляемую CollabNet. <h3>Не уничтожайте Виртуальную Машину постоянно</h3> Не уничтожайте постоянно Виртуальную Машину/приводите ее в негодность, не переустанавливайте операционную систему (кроме VM7 или VM8) и не запускайте ботов, которые это делают. <h3>Никакого крипто-майнинга</h3> Не майните криптовалюту на виртуальной машине, если об этом узнают, вы будете забанены. Вы все равно вы не получите за это денег. <h3>Предупреждение о NSFW</h3> NSFW-контент разрешен только на Вертуальной Машине \"Анархия\" (ВМ0) . На всех остальных вертуальный машинах NSFW контент запрщен и блокирует через E2Guarden.",
"kSiteButtons_Home": "Главная",
"kSiteButtons_FAQ": "ЧаВо",
"kSiteButtons_Rules": "Правила",
"kSiteButtons_DarkMode": "Темная тема",
"kSiteButtons_LightMode": "Светлая тема",
"kSiteButtons_Languages": "Языки",
"kVM_UsersOnlineText": "Пользователи Онлайн",
"kVM_TurnTimeTimer": "Твой ход закончиться через {0} секунд(у)",
"kVM_WaitingTurnTimer": "Твой ход будет через {0} секунд(ы)",
"kVM_VoteCooldownTimer": "Пожалуйста подождите {0} секунд(ы) перед началом голосования за сброс",
"kVM_VoteForResetTitle": "Вы хотите сбросить виртуальную машину?",
"kVM_VoteForResetTimer": "Голосование закончится через {0} секунд(ы)",
"kVMButtons_TakeTurn": "Сделать ход",
"kVMButtons_EndTurn": "Закончить ход",
"kVMButtons_ChangeUsername": "Сменить имя",
"kVMButtons_Keyboard": "Клавиатура",
"KVMButtons_CtrlAltDel": "Ctrl+Alt+Del",
"kVMButtons_VoteForReset": "Голосовать за сброс",
"kVMButtons_Screenshot": "Скриншот",
"kQEMUMonitor": "QEMU Монитор",
"kAdminVMButtons_PassVote": "Восстановить виртуальную машину",
"kAdminVMButtons_CancelVote": "Отменить голосование",
"kAdminVMButtons_Restore": "Восстановить",
"kAdminVMButtons_Reboot": "Перезагрузить",
"kAdminVMButtons_ClearTurnQueue": "Очистить очередь ходов",
"kAdminVMButtons_BypassTurn": "Обойти чужые ходы",
"kAdminVMButtons_IndefiniteTurn": "Бесконечный ход",
"kAdminVMButtons_Ban": "Забанить",
"kAdminVMButtons_Kick": "Кикнуть",
"kAdminVMButtons_TempMute": "Замутить на время",
"kAdminVMButtons_IndefMute": "Замутить на неопределённое время",
"kAdminVMButtons_Unmute": "Размутить",
"kAdminVMButtons_GetIP": "Получить IP-адрес",
"kVMPrompts_AdminChangeUsernamePrompt": "Введите новый никнейм для {0}",
"kVMPrompts_AdminRestoreVMPrompt": "Вы уверены что хотите восстановить VM?",
"kVMPrompts_EnterNewUsernamePrompt": "Введите новое имя пользователя",
"kError_UnexpectedDisconnection": "Вы были отключены от сервера",
"kError_UsernameTaken": "Этот имя уже существует.",
"kError_UsernameInvalid": "Именах могут только иметь цифры, буквы, пробелы, тере, нижнее подчеркивание, и точек, и должны быть от 3 до 20 букв.",
"kError_UsernameBlacklisted": "Этот имя запрещёно.",
"kError_IncorrectPassword": "Неверный пароль.",
"kAccountModal_Verify": "Проверить электронною почту",
"kAccountModal_AccountSettings": "Настройки аккаунта",
"kAccountModal_ResetPassword": "Сбросить пароль",
"kAccountModal_NewPassword": "Новый пароль",
"kAccountModal_ConfirmNewPassword": "Принять новый пароль",
"kAccountModal_CurrentPassword": "Текущий пароль",
"kAccountModal_ConfirmPassword": одтвердите пароль",
"kMissingCaptcha": "Пожалуйста, пройдите капчу.",
"kPasswordsMustMatch": "Пароли должны быть одинаковы.",
"kAccountModal_VerifyText": "Мы отправили письмо на {0}. Чтобы подтвердить нашу аккаунт, пожалуйста веди 8-значный код из письма ниже.",
"kAccountModal_VerifyPasswordResetText": "Мы отправили письмо на {0}. Чтобы сбросить ваш пароль, пожалуйста веди 8-значный код из письма ниже.",
"kAccountModal_PasswordResetSuccess": "Пароль сброшен успешно! Теперь, вы можёте войти с новым паролем.",
"kNotLoggedIn": "Не авторизован"
}
}

98
static/lang/tok.json Normal file
View File

@ -0,0 +1,98 @@
{
"languageName": "Toki Pona",
"translatedLanguageName": "toki pona",
"flag": "🌐",
"author": "Computernewb",
"stringKeys": {
"kGeneric_CollabVM": "ilo CollabVM",
"kGeneric_Yes": "lon",
"kGeneric_No": "ala",
"kGeneric_Ok": "pona",
"kGeneric_Cancel": "o ala",
"kGeneric_Send": "o pana",
"kGeneric_Understood": "mi sona",
"kGeneric_Username": "nimi",
"kGeneric_Password": "nimi len",
"kGeneric_Login": "o lon sijelo",
"kGeneric_Register": "o pali e sijelo",
"kGeneric_EMail": "o toki E-mail",
"kGeneric_DateOfBirth": "tenpo pi kama lon",
"kGeneric_VerificationCode": "sitelen pi pona sona",
"kGeneric_Verify": "o pona e sona",
"kGeneric_Update": "o sin",
"kGeneric_Logout": "o weka tan sijelo",
"kWelcomeModal_Header": "ilo CollabVM la o kama pona",
"kWelcomeModal_Body": "<p>open la o sona e lawa mi:</p> <h3>nanpa wan la o ike ala tawa lawa ma.</h3> kepeken ilo mi la o ike ala tawa lawa pi ma Mewika, tawa lawa pi ma Nujo, tawa lawa pi kulupu ma. mi kama sona e ike ni sina la, mi weka e sina tan ilo. mi o toki tawa kulupu lawa ma la, mi toki.<br><br>mi kama sona e sitelen unpa pi jan lili lon ilo mi la, lawa ma la mi o toki tawa kulupu pi lawa ma.<br><br>sina majuna pi tenpo sike 13 ala la, lawa COPPA la o kepeken ala ilo CollabVM. <h3>nanpa tu la o pakala ala e ilo Internet ante kepeken ilo mi.</h3><!-- Rest omitted because redundant sentences do not translate well in toki pona. --> <h3>nanpa tu la o pana ala e jaki.</h3> kepeken ilo mi la o jaki ala kepeken ilo E-mail, kepeken ilo ante. <h3>nanpa tu tu la o ike ala kepeken pakala ilo.</h3> sina lukin e pakala ilo la, o ike ala kepeken ona. toki tawa mi kepeken nimi E-mail \"computernewbab@gmail.com\" <h3>nanpa luka la nasin ike la o sama ala jan ante.</h3> sina ni la, mi weka e sina lon tenpo lili. mi o weka e sina lon tenpo ale la, mi ni. <h3>nanpa luka wan la jan wan li ken toki e wile lon tenpo wan.</h3> o weka ala tan lawa ni kepeken ilo. sina toki e wile lon tenpo mute la, mi weka e sina. <h3>nanpa luka tu la o kepeken ala ilo pi lawa ilo.</h3> ala la o kepeken ilo DarkComet, kepeken ilo NanoCore, kepeken ilo Anydesk, kepeken ilo TeamViewer, kepeken ilo Orcus, kepeken ilo ante sama ni. <h3>nanpa luka tu wan la mi weka e ken la, o alasa ala e ni.</h3> ni la sina wile pali ike tawa lawa nanpa wan, anu tawa lawa nanpa tu, anu tawa lawa nanpa luka tu la, ni li ike mute. sina wile kepeken ilo jaki pi kepeken mute la, ni li ike mute.<!-- this is a bad translation but it's a badly-written rule --> <h3>lawa nanpa luka tu tu la o pakala suli ala e ilo.</h3> pakala ilo la o weka ala e ken pali ilo. ilo li ilo nanpa luka tu ala, li ilo nanpa luka tu wan ala la, o sin ala e nasin ilo, o kepeken ala ilo pi sin nasin. ilo li kepeken mute ilo sitelen, kepeken mute ilo wile la, ona li ni. <h3>lawa nanpa luka luka la o alasa ala e mani nanpa</h3> sina ni la, mi weka e sina pi tenpo wan. sina awen alasa, mi weka e sina pi tenpo ale. kin sina kama ala jo e mani tan ni. <h3>unpa la</h3> o sona e ni: ilo 0b0t la sitelen unpa li ken, li lon mute. mi alasa weka e sitelen unpa tan ilo ante. taso tenpo la ona li kama.",
"kSiteButtons_Home": "tomo",
"kSiteButtons_FAQ": "wile sona",
"kSiteButtons_Rules": "lawa",
"kSiteButtons_DarkMode": "pimeja",
"kSiteButtons_LightMode": "walo",
"kSiteButtons_Languages": "toki",
"kVM_UsersOnlineText": "jan ni li lon:",
"kVM_TurnTimeTimer": "ken pali li weka lon tenpo lili {0}.",
"kVM_WaitingTurnTimer": "sina ken pali lon tenpo lili {0}.",
"kVM_VoteCooldownTimer": "sina wile open sin e toki wile la, o awen lon tenpo lili {0}.",
"kVM_VoteForResetTitle": "sina wile ala wile sin e ilo?",
"kVM_VoteForResetTimer": "toki wile li pini lon tenpo lili {0}",
"kVMButtons_TakeTurn": "o pali",
"kVMButtons_EndTurn": "o pini e pali",
"kVMButtons_ChangeUsername": "o ante e nimi",
"kVMButtons_Keyboard": "ilo sitelen",
"KVMButtons_CtrlAltDel": "sitelen Ctrl+Alt+Del",
"kVMButtons_VoteForReset": "o wile e sin ilo",
"kVMButtons_Screenshot": "o jo e sitelen ni",
"kQEMUMonitor": "sinpin QEMU",
"kAdminVMButtons_PassVote": "mi pilin ala",
"kAdminVMButtons_CancelVote": "ni li wile mi ala",
"kAdminVMButtons_Restore": "o sin",
"kAdminVMButtons_Reboot": "o pini, o open",
"kAdminVMButtons_ClearTurnQueue": "o weka e nasin pali",
"kAdminVMButtons_BypassTurn": "o pana ala e pali",
"kAdminVMButtons_IndefiniteTurn": "pali pi tenpo ale",
"kAdminVMButtons_Ban": "o weka pi tenpo ale",
"kAdminVMButtons_Kick": "o weka pi tenpo wan",
"kAdminVMButtons_TempMute": "tenpo la o weka e ken toki",
"kAdminVMButtons_IndefMute": "ale la o weka e ken toki",
"kAdminVMButtons_Unmute": "o sin e ken toki",
"kAdminVMButtons_GetIP": "o sona e sitelen IP",
"kVMPrompts_AdminChangeUsernamePrompt": "jan {0} la o pana e nimi sin:",
"kVMPrompts_AdminRestoreVMPrompt": "sina wile ala wile sin e ilo?",
"kVMPrompts_EnterNewUsernamePrompt": "o pana e nimi. ala la, mi pana e nimi pi jan sin",
"kError_UnexpectedDisconnection": "sina weka tan ilo.",
"kError_UsernameTaken": "jan ante li jo e nimi ni",
"kError_UsernameInvalid": "nimi li ken jo taso e sitelen [A-Za-z0-9 \\-_.]. sitelen tu li lili ike. siten mute wan li suli ike.",
"kError_UsernameBlacklisted": "nimi ni li ken ala.",
"kError_IncorrectPassword": "ni li nimi len sina ala.",
"kAccountModal_Verify": "nimi E-mail la o pona e sona",
"kAccountModal_AccountSettings": "wile sijelo",
"kAccountModal_ResetPassword": "o ante e nimi len",
"kAccountModal_NewPassword": "nimi len sin",
"kAccountModal_ConfirmNewPassword": "sin la nimi len sin",
"kAccountModal_CurrentPassword": "nimi len lon",
"kAccountModal_ConfirmPassword": "sin la nimi len sin",
"kMissingCaptcha": "o toki e sitelen CAPTCHA.",
"kPasswordsMustMatch": "nimi len o sama.",
"kAccountModal_VerifyText": "mi toki E-mail tawa nimi {0}. pona sona la o pana e sitelen luka tu wan tan ni.",
"kAccountModal_VerifyPasswordResetText": "mi toki E-mail tawa nimi {0}. ante pi nimi len la o pana e sitelen luka tu wan tan ni.",
"kAccountModal_PasswordResetSuccess": "nimi len sina li kama ante. sina ken lon sijelo kepeken ona.",
"kNotLoggedIn": "sina lon sijelo ala"
}
}

98
static/lang/uk-ua.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -41,7 +41,7 @@
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
"resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */