add invites

This commit is contained in:
_Bastler 2021-05-06 21:26:57 +02:00
parent 3e02cbb353
commit c8a10eb73c
12 changed files with 1213 additions and 892 deletions

View File

@ -23,6 +23,7 @@ import {UserComponent} from './pages/user/user.component'
import {JitsiComponent} from './pages/jitsi/jitsi.component'
import {AliasesComponent} from './pages/account/aliases/aliases.component';
import {DomainsComponent} from './pages/account/domains/domains.component';
import {InvitesComponent} from './pages/invites/invites.component';
const routes: Routes = [
{path: '', redirectTo: "/services", pathMatch: 'full'},
@ -50,13 +51,14 @@ const routes: Routes = [
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
{path: 'tokens', component: TokensComponent, canActivate: [AuthGuard]},
{path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard]},
{path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard]},
{path: 'unavailable', component: UnavailableComponent},
{path: 'p/:username', component: UserComponent, canActivate: [AuthUpdateGuard]},
{path: '**', component: NotfoundComponent, pathMatch: 'full', canActivate: [AuthUpdateGuard]},
];
@NgModule({
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload', relativeLinkResolution: 'legacy' })],
imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload', relativeLinkResolution: 'legacy'})],
exports: [RouterModule]
})
export class AppRoutingModule {}

View File

@ -19,6 +19,7 @@ import {LoginTotpComponent} from './pages/login-totp/login-totp.component';
import {FormLoginComponent} from './pages/form-login/form-login.component';
import {FormLoginTotpComponent} from './pages/form-login-totp/form-login-totp.component';
import {TokensComponent} from './pages/tokens/tokens.component';
import {InvitesComponent} from './pages/invites/invites.component';
import {PermissionsComponent} from './ui/permissions/permissions.component';
import {ProfileFieldDialog, ProfileFieldsComponent, ProfileFieldBlob} from './ui/profilefields/profilefields.component';
import {QuotasComponent} from './ui/quotas/quotas.component';
@ -70,6 +71,7 @@ export class XhrInterceptor implements HttpInterceptor {
FormLoginComponent,
FormLoginTotpComponent,
TokensComponent,
InvitesComponent,
ServicesComponent,
PermissionsComponent,
ProfileFieldsComponent, ProfileFieldDialog, ProfileFieldBlob,

View File

@ -0,0 +1,63 @@
<h3>{{'invites' | i18n}}</h3>
<table mat-table matSort [dataSource]="invites">
<ng-container matColumnDef="starts">
<th mat-header-cell *matHeaderCellDef> {{'invite.starts' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> {{ invite.starts | date:datetimeformat}} </td>
</ng-container>
<ng-container matColumnDef="expires">
<th mat-header-cell *matHeaderCellDef> {{'invite.expires' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> {{ invite.expires | date:datetimeformat}} </td>
</ng-container>
<ng-container matColumnDef="link">
<th mat-header-cell *matHeaderCellDef> {{'invite.link' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <a href="{{ invite.codeLink}}" target="_blank" mat-button color="accent">
{{
invite.code}}</a> </td>
</ng-container>
<ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> {{ invite.note}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="inviteColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: inviteColumns"></tr>
</table>
<mat-card>
<mat-card-content>
<p>{{'invites.info' | i18n}}</p>
<p *ngIf="!inviteQuota">{{'invites.noQuota' | i18n}}</p>
<div *ngIf="inviteQuota">
<p>{{'invites.left' | i18n:inviteQuota}}</p>
</div>
</mat-card-content>
<mat-card-actions>
<button *ngIf="inviteQuota && !working" mat-raised-button color="primary" (click)="create()">
{{'invite.create' | i18n}}
</button>
</mat-card-actions>
</mat-card>
<div *ngIf="others && others.content">
<h4>{{'invites.others' | i18n}}</h4>
<mat-form-field>
<input matInput [formControl]="searchFormControl" placeholder="{{'invites.search' | i18n}}">
</mat-form-field>
<table mat-table matSort [dataSource]="others.content">
<ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> {{ invite.note}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="otherColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: otherColumns"></tr>
</table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="others.totalElements" [pageSize]="others.size" (page)="updateOthers($event)" showFirstLastButtons></mat-paginator>
</div>

View File

@ -0,0 +1,3 @@
mat-form-field {
display: block;
}

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InvitesComponent } from './invites.component';
describe('InvitesComponent', () => {
let component: InvitesComponent;
let fixture: ComponentFixture<InvitesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ InvitesComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(InvitesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,99 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {PageEvent} from '@angular/material/paginator';
import {AuthService} from '../../services/auth.service';
import {I18nService} from '../../services/i18n.service';
import {QuotaService} from '../../services/quota.service';
import {InviteService} from '../../services/invites.service';
import {FormControl} from '@angular/forms';
import {debounceTime} from 'rxjs/operators';
@Component({
selector: 'app-invites',
templateUrl: './invites.component.html',
styleUrls: ['./invites.component.scss']
})
export class InvitesComponent implements OnInit {
quota: string;
invites: any[];
others: any;
inviteQuota: number = 0;
success: boolean;
working: boolean;
datetimeformat: string;
pageSizeOptions: number[] = [5, 10, 25, 50];
searchFormControl = new FormControl();
inviteColumns = ["starts", "expires", "link", "note"];
otherColumns = ["note"];
constructor(
private authService: AuthService,
private inviteService: InviteService,
private i18n: I18nService,
private quotaService: QuotaService,
private router: Router,
private route: ActivatedRoute) {
}
async ngOnInit() {
this.datetimeformat = this.i18n.get('format.datetime', []);
this.quota = this.route.snapshot.paramMap.get('quota');
this.searchFormControl.valueChanges.pipe(debounceTime(500)).subscribe(value => {
this.inviteService.getOthersPages(this.quota, 0, this.others.size, value).subscribe((data: any) => {
this.others = data;
}, (error) => {})
})
this.update();
}
update(): void {
this.inviteQuota = 0;
this.quotaService.quotas().subscribe((data: any) => {
for(let quota of data) {
if(quota.name == "invite_" + this.quota) {
this.inviteQuota = quota.value;
}
}
})
this.inviteService.get(this.quota).subscribe((data: any) => {
this.invites = data;
})
this.inviteService.getOthers(this.quota).subscribe((data: any) => {
this.others = data;
}, (error) => {})
}
create(): void {
this.working = true;
this.inviteService.create(this.quota, {}).subscribe(response => {
this.update();
this.working = false;
}, (error) => {
this.working = false;
if(error.status == 409) {
let errors = {};
for(let code of error.error) {
errors[code.field] = errors[code.field] || {};
errors[code.field][code.code] = true;
}
}
})
}
updateOthers(event: PageEvent) {
this.inviteService.getOthersPages(this.quota, event.pageIndex, event.pageSize, "").subscribe((data: any) => {
this.others = data;
}, (error) => {})
}
}

View File

@ -2,24 +2,34 @@
<p *ngIf="!services || services.length == 0">{{'services.empty' | i18n}}</p>
<div fxLayout="row wrap" fxLayoutGap="16px grid">
<div fxFlex="33.33%" fxFlex.sm="50%" fxFlex.xs="100%" *ngFor="let service of services">
<mat-card>
<mat-card-header>
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
<mat-card-title>{{'services.' + service.name + '.title' | i18n}}</mat-card-title>
<mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>
{{ 'services.' + service.name + '.text' | i18n}}
</p>
</mat-card-content>
<mat-card-actions>
<a href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'" mat-raised-button
color="accent">{{'services.goto' | i18n}} <mat-icon *ngIf="!service.sameSite" inline="true">
open_in_new</mat-icon></a>
</mat-card-actions>
</mat-card>
</div>
</div>
<table *ngIf="services && services.length > 0" mat-table matSort [dataSource]="services" (matSortChange)="sortData($event)" matSortActive="name" matSortDirection="desc">
<ng-container matColumnDef="icon">
<th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let service">
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'service.name' | i18n}} </th>
<td mat-cell *matCellDef="let service" class="text-center">
<a href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'" mat-button color="accent">
<strong>{{'services.' + service.name + '.title' | i18n}}</strong>
<mat-icon *ngIf="!service.sameSite" inline="true">
open_in_new</mat-icon>
</a>
<div><small>{{'services.' + service.name + '.subtitle' | i18n}}</small></div>
</td>
</ng-container>
<ng-container matColumnDef="text">
<th mat-header-cell *matHeaderCellDef> {{'service.text' | i18n}} </th>
<td mat-cell *matCellDef="let service">{{ 'services.' + service.name + '.text' | i18n}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="serviceColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: serviceColumns"></tr>
</table>

View File

@ -1,5 +1,7 @@
import { Component, OnInit, Input } from '@angular/core';
import { ServiceService } from '../../services/service.service';
import {Component, OnInit} from '@angular/core';
import {Sort} from '@angular/material/sort';
import {ServiceService} from '../../services/service.service';
import {I18nService} from '../../services/i18n.service';
@Component({
selector: 'app-services',
@ -9,13 +11,34 @@ import { ServiceService } from '../../services/service.service';
export class ServicesComponent implements OnInit {
services = [];
serviceColumns = ["icon", "name", "text"];
constructor(private serviceService: ServiceService) { }
constructor(private serviceService: ServiceService, private i18n: I18nService) {}
ngOnInit(): void {
this.serviceService.services().subscribe((data: any) => {
this.services = data;
this.sortData({"active": "name", "direction": "desc"});
})
}
sortData(sort: Sort) {
const data = this.services.slice();
if(!sort.active || sort.direction === '') {
this.services = data;
return;
}
this.services = data.sort((a, b) => {
const isAsc = sort.direction === 'asc';
switch(sort.active) {
case 'name': return this.compare(this.i18n.get('services.' + a.name + '.title', []), this.i18n.get('services.' + b.name + '.title', []), isAsc);
default: return 0;
}
});
}
compare(a: number | string | String, b: number | string | String, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
}

View File

@ -0,0 +1,35 @@
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {environment} from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class InviteService {
constructor(private http: HttpClient) {
}
get(quota: string) {
return this.http.get(environment.apiUrl + "/invites" + (quota ? "?quota=" + quota : ""));
}
getOthers(quota: string) {
return this.http.get(environment.apiUrl + "/invites/" + quota + "/others");
}
getOthersPages(quota: string, page : number, size : number, search : string) {
return this.http.get(environment.apiUrl + "/invites/" + quota + "/others?page=" + page + "&size=" + size + "&search=" + search);
}
create(quota: string, invite: any) {
return this.http.post(environment.apiUrl + "/invites/" + quota, invite);
}
update(invite: any) {
return this.http.post(environment.apiUrl + "/invites", invite);
}
}

View File

@ -1,5 +1,4 @@
<table mat-table matSort [dataSource]="profileFields" (matSortChange)="sortData($event)" matSortActive="index"
matSortDirection="asc">
<table mat-table matSort [dataSource]="profileFields" (matSortChange)="sortData($event)" matSortActive="index" matSortDirection="asc">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'profileField.name' | i18n}} </th>
@ -15,11 +14,9 @@
<span *ngSwitchCase="'DATETIME'">{{profileField.value | date:datetimeformat}}</span>
<span *ngSwitchCase="'TIME'">{{profileField.value | date:timeformat}}</span>
<a *ngSwitchCase="'URL'" class="accent" href="{{profileField.value}}">{{profileField.value}}</a>
<a *ngSwitchCase="'EMAIL'" class="accent"
href="mailto:{{profileField.value}}">{{profileField.value}}</a>
<a *ngSwitchCase="'EMAIL'" class="accent" href="mailto:{{profileField.value}}">{{profileField.value}}</a>
<span *ngSwitchCase="'NUMBER'">{{profileField.value}}</span>
<button *ngSwitchCase="'BLOB'" mat-raised-buttonu
(click)="openBlob(profileField)">{{'profileField.openBlob' | i18n}}</button>
<button *ngSwitchCase="'BLOB'" mat-raised-button (click)="openBlob(profileField)">{{'profileField.openBlob' | i18n}}</button>
<mat-slide-toggle *ngSwitchCase="'BOOL'" [checked]="profileField.value == 'true'" disabled>
</mat-slide-toggle>
</div>

View File

@ -1,457 +1,488 @@
{
"account": {
".": "Account",
"advanced": {
".": "Erweitert"
}
},
"cancel": "Abbrechen",
"close": "Schließen",
"confirm": "Bestätigen",
"email": {
".": "E-Mail Adresse",
"invalid": "ungültige E-Mail Adresse",
"primary": {
".": "primäre E-Mail Adresse",
"hint": "Eine primäre E-Mail Adresse dient dazu eine andere Kontaktmöglichkeit als deine we.bstly Adresse anzugeben."
}
},
"format": {
"date": "dd.MM.yyyy",
"datetime": "dd.MM.yyyy HH:mm",
"time": "HH:mm:ss"
},
"greet": "Hallo {0}",
"help": "Hilfe",
"imprint": "Impressum",
"info": {
".": "Info"
},
"jitsi": {
"rooms": {
".": "Jitsi Räume",
"confirmDelete": "Möchtest du wirklich deinen Jitsi Raum '{0}' löschen?",
"create": "Jitsi Raum erstellen",
"delete": "Löschen",
"error": {
"expires": "Ungültiges Ende.",
"moderationStarts" : "Ungültiger Beginn Moderation. Moderation muss vor Beginn liegen.",
"room": "Bitte gebe einen anderen Namen an. Der Name ist schon vergeben oder enthält ungültige Zeichen. Erlaubt sind nur Buchstaben und Zahlen.",
"starts": "Ungültiger Beginn."
},
"expires": "Ende",
"info": "Du kannst hier Jitsi Räume erstellen. Die Anzahl wird über eine Quota begrenzt.",
"left": "Du kannst noch {0} Jitsi Raum/Räume erstellen.",
"moderationUrl": "Url für Moderation",
"moderationStarts" : "Beginn Moderation",
"noQuota": "Deine Quota für Jitsi Räume ist leider aufgebraucht.",
"notStarted" : "Die Konferenz hat noch nicht begonnen.",
"room": "Name",
"starts": "Beginn"
"account": {
".": "Account",
"advanced": {
".": "Erweitert"
}
},
"share": {
".": "Teilen",
"clipboard": {
"copied": "In die Zwischenablage kopiert",
"text": "Text in Zwischenablage kopieren",
"url": "Url in Zwischenablage kopieren"
},
"email": {
".": "Via E-Mail teilen",
"subject": "Einladung in Videokonferenz {0}"
},
"text" : {
"both" : "Die Konferenz beginnt am {0} und endet am {1}.",
"expires" : "Die Konferenz endet am {0}.",
"intro" : "Hallo, ich möchte dich zu einer Videokonferenz einladen.",
"outro" : "Du kannst der Konferenz unter folgendem Link beitreten:\n {0}",
"starts" : "Die Konferenz beginnt am {0}."
}
}
},
"locale": {
"de-informal": {
"long": "Deutsch",
"short": "DE"
"cancel": "Abbrechen",
"close": "Schließen",
"confirm": "Bestätigen",
"email": {
".": "E-Mail Adresse",
"invalid": "ungültige E-Mail Adresse",
"primary": {
".": "primäre E-Mail Adresse",
"hint": "Eine primäre E-Mail Adresse dient dazu eine andere Kontaktmöglichkeit als deine we.bstly Adresse anzugeben."
}
},
"en": {
"long": "English",
"short": "EN"
}
},
"login": {
".": "Login",
"external": "Login",
"invalid": "Falscher Username oder Passwort.",
"keepSession": "Eingeloggt bleiben"
},
"logout": "Logout",
"not-found": {
".": "Nicht gefunden",
"text": "Diese Seite wurde nicht gefunden."
},
"ok": "Ok",
"password": {
".": "Passwort",
"change": "Passwort ändern",
"changed": "Passwort erfolgreich geändert",
"confirm": "Passwort bestätigen",
"current": "Akutelles Passwort",
"error": {
"ILLEGAL_WHITESPACE": "Bitte keine Leerzeichen verwenden.",
"INSUFFICIENT_DIGIT": "Bitte mindestens eine Zahl eingeben.",
"INSUFFICIENT_LOWERCASE": "Bitte mindestens einen Kleinbuchstaben eingeben.",
"INSUFFICIENT_SPECIAL": "Bitte mindestens ein Sonderzeichen eingeben.",
"INSUFFICIENT_UPPERCASE": "Bitte mindestens einen Großbuchstaben eingeben.",
"TOO_SHORT": "Bitte ein längeres Passwort wählen.",
"NOT_MATCH": "Passwörter stimmen nicht überein."
"format": {
"date": "dd.MM.yyyy",
"datetime": "dd.MM.yyyy HH:mm",
"time": "HH:mm:ss"
},
"forgot": "Passwort vergessen",
"invalid": {
"hint": "Bitte gebe das Passwort in einem gültigen Format an."
"greet": "Hallo {0}",
"help": "Hilfe",
"imprint": "Impressum",
"info": {
".": "Info"
},
"not-match": "Passwörter stimmen nicht überein.",
"request": "Neues Passwort anfordern",
"reset": {
".": "Passwort setzen",
"login": "Zum Login",
"success": {
"text": "Dein neues Passwort wurde übernommen. Du kannst dich nun mit deinem neuen Passwort einloggen.",
"title": "Passwort erfolgreich geändert"
}
}
},
"permissions": {
".": "Berechtigungen",
"expires": "Gültig bis",
"name": "Name",
"starts": "Gültig ab"
},
"pgp": {
".": "PGP",
"privateKey": {
".": "Privater PGP Schlüssel",
"confirmStore": "Ich habe meinen privaten Schlüssel sicher gespeichert!",
"downloadKey": "Privaten Schlüssel herunterladen",
"help": "Dein Privater Schlüssel wird aktuell verwendet, wenn du dein Passwort vergessen hast. Außerdem kannst du damit E-Mails entschlüsseln und dich gegenüber einigen Diensten authentifizieren.",
"downloaded": "Privaten Schlüssel anderweitig gesichert.",
"copyKey": "In die Zwischenablage kopieren"
}
},
"privacy-policy": "Datenschutzerklärung",
"profile": "Profil",
"profileField": {
".": "Profilfeld",
"confirmDelete": "Möchtest du das Profilfeld '{0}' wirklich löschen?",
"create": "Neues Profilfeld hinzufügen",
"delete": "Löschen",
"edit": "Bearbeiten",
"error": {
"BLOB": {
".": "Kein gültiger Binärblob"
},
"BOOL": {
".": "Kein gültiger Boolean"
},
"DATE": {
".": "Kein gültiges Datum"
},
"DATETIME": {
".": "Kein gültiges Datum oder Uhrzeit"
},
"EMAIL": {
".": "Keine gültige E-Mail Adresse"
},
"name": "Name zu kurz oder ungültig",
"NUMBER": {
".": "Keine gültige Nummer"
},
"TEXT": {
".": "Textfeld zu lang"
},
"TIME": {
".": "Keine gültige Uhrzeit"
},
"type": "Ungültiger Typ für dieses Profilfeld",
"URL": {
".": "Keine gültige URL"
},
"visibility": "Keine gültige Sichtbarkeit für Profilfeld"
"invite": {
".": "Einleidung",
"create": "Einladung erstellen",
"expires": "Gültig bis",
"link": "Link",
"note": "Notiz",
"starts": "Gültig ab"
},
"index": "Index",
"name": {
".": "Key",
"darkTheme": "Dunkles Thema",
"email": "E-Mail Adresse",
"locale": "Sprache",
"primaryEmail": "E-Mail Adresse primär verwenden",
"prtyMap": "Partey Karte",
"publicKey": "Öffentlicher PGP Schlüssel"
"invites": {
".": "Einladungen",
"info": "Hier kannst du neue Einladungen erstellen. Um die Einladung zu bearbeiten klicke einfach auf den Link. Wenn du authoriziert bist, kannst du dort direkt den persönlichen Einladungstext bearbeiten oder eine Notiz hinzufügen.",
"left": "Du kannst noch {0} Einladungen erstellen.",
"others": "Einladungen anderer Mitglieder",
"search": "Suche"
},
"openBlob": "Anzeigen",
"type": {
".": "Typ",
"BLOB": {
".": "Binärblob"
},
"BOOL": {
".": "Boolean"
},
"DATE": {
".": "Datum"
},
"DATETIME": {
".": "Datum mit Uhrzeit"
},
"EMAIL": {
".": "E-Mail"
},
"NUMBER": {
".": "Numerisch"
},
"TEXT": {
".": "Textfeld"
},
"TIME": {
".": "Uhrzeit"
},
"URL": {
".": "URL"
}
},
"value": "Wert"
},
"quotas": {
".": "Quotas",
"name": "Name",
"unit": {
"#": "# (Anzahl)",
".": "Einheit",
"G": "GB (Gigabyte)"
},
"value": "Quota"
},
"register": {
".": "Registrierung",
"login": "Zum Login",
"success": {
"text": "Deine Registrierung war erfolgreich. Du kannst dich nun einloggen!",
"title": "Registrierung abgeschlossen"
},
"token.missing": "Du benötigst leider ein gültiges Token!"
},
"save": "Speichern",
"security": {
".": "Sicherheit",
"2fa": {
".": "Zwei-Faktor-Authentifizierung (2FA)",
"info": "Du kannst hier einen zweiten Faktor zusätzlich zu deinem Passwort hinzufügen. Beachte, dass dies nur den Login in deinen we.bstly-Account betrifft. 2FA gilt nicht für deinen E-Mail Account. Aktuell wird nur TOTP (bekannt als Google Authenticator) unterstützt.",
"totp": {
".": "2FA (TOTP)",
"activate": "Um TOTP als 2FA zu aktivieren, gebe bitte deinen aktuellen Code ein.",
"code": "TOTP Code",
"create": "2FA (TOTP) einrichten",
"enable": "Aktiviere 2FA (TOTP)",
"external": "2FA (TOTP)",
"hint": "Um TOTP als zweiten Faktor beim Login zu verwenden, scanne den QRCode mit deiner TOTP App.",
"invalid": "TOTP Code ist ungültig",
"keepSession": "2FA (TOTP) für dieses Gerät merken",
"login": "Code verfizieren",
"missing": "Bitte TOTP Code eingeben",
"remove": "2FA (TOTP) deaktivieren"
}
},
"status": {
".": "Status",
"change": "Status aktualisieren",
"hint": "Dein User Status gibt an, wie mit deinen Account Daten umgegangen wird wenn deine Berechtigungen ausgelaufen sind.",
"NORMAL": {
".": "Normal",
"hint": "Dein Account sowie alle gespeicherten Daten werden vier (4) Wochen nach Ablauf gelöscht. Du hast also vier Wochen Zeit deinen Account wieder zu reaktivieren."
},
"PURGE": {
".": "Löschen",
"hint": "Dein Account sowie alle gespeicherten Daten werden umgehend(!) nach Ablauf gelöscht. Du kannst deinen Account nicht(!) mehr reaktivieren."
},
"SLEEP": {
".": "Schlafen",
"hint": "Dein Account sowie alle gespeicherten Daten werden nicht(!) gelöscht. Du kannst deinen Account also jederzeit wieder reaktivieren."
},
"success": "Status erfolgreich geändert"
}
},
"service-unavailable": {
".": "Dienst nicht erreichbar",
"retry": "Seite neu laden",
"support": "Support",
"text": "Zurzeit scheint der Dienst nicht erreichbar zu sein. Wenn diese Meldung länger besteht, melde dich beim Support!"
},
"services": {
".": "Dienste",
"alias_creation": {
"icon": "alternate_email",
"subtitle": "Anlegen von alternativen Namen",
"text": "Du kannst zusätzlich zu deinem Usernamen noch alternative Namen anlegen.",
"title": "Alternative Namen"
},
"empty": "Du hast aktuell keine Berechtigungen zur Nutzung von Diensten.",
"gitea": {
"icon": "code",
"subtitle": "Git-Repositories",
"text": "Alternative zu Diensten wie GitHub, Source Code von bstly-Entwicklungen",
"title": "Gitea"
},
"goto": "Zum Dienst",
"jitsi": {
"icon": "video_call",
"subtitle": "Video Konferenzen",
"text": "Video Konferenzen mit allen Funktionen für Online-Treffen mit Video und Audio Streams.",
"title": "Jitsi Meet"
"rooms": {
".": "Jitsi Räume",
"confirmDelete": "Möchtest du wirklich deinen Jitsi Raum '{0}' löschen?",
"create": "Jitsi Raum erstellen",
"delete": "Löschen",
"error": {
"expires": "Ungültiges Ende.",
"moderationStarts": "Ungültiger Beginn Moderation. Moderation muss vor Beginn liegen.",
"room": "Bitte gebe einen anderen Namen an. Der Name ist schon vergeben oder enthält ungültige Zeichen. Erlaubt sind nur Buchstaben und Zahlen.",
"starts": "Ungültiger Beginn."
},
"expires": "Ende",
"info": "Du kannst hier Jitsi Räume erstellen. Die Anzahl wird über eine Quota begrenzt.",
"left": "Du kannst noch {0} Jitsi Raum/Räume erstellen.",
"moderationUrl": "Url für Moderation",
"moderationStarts": "Beginn Moderation",
"noQuota": "Deine Quota für Jitsi Räume ist leider aufgebraucht.",
"notStarted": "Die Konferenz hat noch nicht begonnen.",
"room": "Name",
"starts": "Beginn"
},
"share": {
".": "Teilen",
"clipboard": {
"copied": "In die Zwischenablage kopiert",
"text": "Text in Zwischenablage kopieren",
"url": "Url in Zwischenablage kopieren"
},
"email": {
".": "Via E-Mail teilen",
"subject": "Einladung in Videokonferenz {0}"
},
"text": {
"both": "Die Konferenz beginnt am {0} und endet am {1}.",
"expires": "Die Konferenz endet am {0}.",
"intro": "Hallo, ich möchte dich zu einer Videokonferenz einladen.",
"outro": "Du kannst der Konferenz unter folgendem Link beitreten:\n {0}",
"starts": "Die Konferenz beginnt am {0}."
}
}
},
"mail": {
"icon": "email",
"subtitle": "E-Mail Konto",
"text": "Catch-All an @{username}.we.bstly.de, lernender Spam-Filter und PGP Verschlüsselung.",
"title": "E-Mail Postfach"
"locale": {
"de-informal": {
"long": "Deutsch",
"short": "DE"
},
"en": {
"long": "English",
"short": "EN"
}
},
"matrix": {
"icon": "question_answer",
"subtitle": "Messenger Plattform",
"text": "mit anderen Austauschen, sich Informieren oder einfach quatschen.",
"title": "Matrix"
"login": {
".": "Login",
"external": "Login",
"invalid": "Falscher Username oder Passwort.",
"keepSession": "Eingeloggt bleiben"
},
"nextcloud": {
"icon": "cloud",
"subtitle": "Cloud Plattform",
"text": "Dateiverwaltung, Kalender, Aufgabenmanagement, Kontaktmanagement, Abstimmungen und mehr.",
"title": "Nextcloud"
"logout": "Logout",
"not-found": {
".": "Nicht gefunden",
"text": "Diese Seite wurde nicht gefunden."
},
"owncast": {
"icon": "videocam",
"subtitle": "Livestreams",
"text": "Livestreams von Bastelei e. V.",
"title": "Owncast"
"ok": "Ok",
"password": {
".": "Passwort",
"change": "Passwort ändern",
"changed": "Passwort erfolgreich geändert",
"confirm": "Passwort bestätigen",
"current": "Akutelles Passwort",
"error": {
"ILLEGAL_WHITESPACE": "Bitte keine Leerzeichen verwenden.",
"INSUFFICIENT_DIGIT": "Bitte mindestens eine Zahl eingeben.",
"INSUFFICIENT_LOWERCASE": "Bitte mindestens einen Kleinbuchstaben eingeben.",
"INSUFFICIENT_SPECIAL": "Bitte mindestens ein Sonderzeichen eingeben.",
"INSUFFICIENT_UPPERCASE": "Bitte mindestens einen Großbuchstaben eingeben.",
"TOO_SHORT": "Bitte ein längeres Passwort wählen.",
"NOT_MATCH": "Passwörter stimmen nicht überein."
},
"forgot": "Passwort vergessen",
"invalid": {
"hint": "Bitte gebe das Passwort in einem gültigen Format an."
},
"not-match": "Passwörter stimmen nicht überein.",
"request": "Neues Passwort anfordern",
"reset": {
".": "Passwort setzen",
"login": "Zum Login",
"success": {
"text": "Dein neues Passwort wurde übernommen. Du kannst dich nun mit deinem neuen Passwort einloggen.",
"title": "Passwort erfolgreich geändert"
}
}
},
"partey": {
"icon": "celebration",
"subtitle": "Virtuelles Vereinsheim",
"text": "Digitaler Treffpunkt für Veranstaltungen oder einfach zum Abhängen.",
"title": "Partey"
"permissions": {
".": "Berechtigungen",
"expires": "Gültig bis",
"name": "Name",
"starts": "Gültig ab"
},
"registration_vouchers": {
"icon": "card_giftcard",
"subtitle": "Gutschein Code für Registrierungs-Token",
"text": "Einladung um die Dienste des Bastelei e. V. zu nutzen",
"title": "Registrierungs-Gutscheincodes"
"pgp": {
".": "PGP",
"privateKey": {
".": "Privater PGP Schlüssel",
"confirmStore": "Ich habe meinen privaten Schlüssel sicher gespeichert!",
"downloadKey": "Privaten Schlüssel herunterladen",
"help": "Dein Privater Schlüssel wird aktuell verwendet, wenn du dein Passwort vergessen hast. Außerdem kannst du damit E-Mails entschlüsseln und dich gegenüber einigen Diensten authentifizieren.",
"downloaded": "Privaten Schlüssel anderweitig gesichert.",
"copyKey": "In die Zwischenablage kopieren"
}
},
"ROLE_ADMIN": {
"icon": "admin_panel_settings",
"subtitle": "Administrator von we.bstly",
"text": "Administrator Rechte für die API von we.bstly",
"title": "Administrator"
"privacy-policy": "Datenschutzerklärung",
"profile": "Profil",
"profileField": {
".": "Profilfeld",
"confirmDelete": "Möchtest du das Profilfeld '{0}' wirklich löschen?",
"create": "Neues Profilfeld hinzufügen",
"delete": "Löschen",
"edit": "Bearbeiten",
"error": {
"BLOB": {
".": "Kein gültiger Binärblob"
},
"BOOL": {
".": "Kein gültiger Boolean"
},
"DATE": {
".": "Kein gültiges Datum"
},
"DATETIME": {
".": "Kein gültiges Datum oder Uhrzeit"
},
"EMAIL": {
".": "Keine gültige E-Mail Adresse"
},
"name": "Name zu kurz oder ungültig",
"NUMBER": {
".": "Keine gültige Nummer"
},
"TEXT": {
".": "Textfeld zu lang"
},
"TIME": {
".": "Keine gültige Uhrzeit"
},
"type": "Ungültiger Typ für dieses Profilfeld",
"URL": {
".": "Keine gültige URL"
},
"visibility": "Keine gültige Sichtbarkeit für Profilfeld"
},
"index": "Index",
"name": {
".": "Key",
"darkTheme": "Dunkles Thema",
"email": "E-Mail Adresse",
"locale": "Sprache",
"primaryEmail": "E-Mail Adresse primär verwenden",
"prtyMap": "Partey Karte",
"publicKey": "Öffentlicher PGP Schlüssel"
},
"openBlob": "Anzeigen",
"type": {
".": "Typ",
"BLOB": {
".": "Binärblob"
},
"BOOL": {
".": "Boolean"
},
"DATE": {
".": "Datum"
},
"DATETIME": {
".": "Datum mit Uhrzeit"
},
"EMAIL": {
".": "E-Mail"
},
"NUMBER": {
".": "Numerisch"
},
"TEXT": {
".": "Textfeld"
},
"TIME": {
".": "Uhrzeit"
},
"URL": {
".": "URL"
}
},
"value": "Wert"
},
"ROLE_GUEST": {
"icon": "schedule_send",
"subtitle": "Gast Account von we.bstly",
"text": "Gast Account Einladung",
"title": "Gast Account"
"quotas": {
".": "Quotas",
"name": "Name",
"unit": {
"#": "# (Anzahl)",
".": "Einheit",
"G": "GB (Gigabyte)"
},
"value": "Quota"
},
"ROLE_MEMBER": {
"icon": "loyalty",
"subtitle": "Mitgliedschaft im Bastelei e. V.",
"text": "Reguläres Mitglied im Bastelei e. V.",
"title": "Vereinsmitgliedschaft"
"register": {
".": "Registrierung",
"login": "Zum Login",
"success": {
"text": "Deine Registrierung war erfolgreich. Du kannst dich nun einloggen!",
"title": "Registrierung abgeschlossen"
},
"token.missing": "Du benötigst leider ein gültiges Token!"
},
"wikijs": {
"icon": "school",
"subtitle": "Informationen, Dokumentation, Anleitungen",
"text": "Alle Information rund um Bastelei e. V. und den angebotenen Diensten, sowie Anleitungen für einzelne Dienste und Funktionen",
"title": "Wiki"
"save": "Speichern",
"security": {
".": "Sicherheit",
"2fa": {
".": "Zwei-Faktor-Authentifizierung (2FA)",
"info": "Du kannst hier einen zweiten Faktor zusätzlich zu deinem Passwort hinzufügen. Beachte, dass dies nur den Login in deinen we.bstly-Account betrifft. 2FA gilt nicht für deinen E-Mail Account. Aktuell wird nur TOTP (bekannt als Google Authenticator) unterstützt.",
"totp": {
".": "2FA (TOTP)",
"activate": "Um TOTP als 2FA zu aktivieren, gebe bitte deinen aktuellen Code ein.",
"code": "TOTP Code",
"create": "2FA (TOTP) einrichten",
"enable": "Aktiviere 2FA (TOTP)",
"external": "2FA (TOTP)",
"hint": "Um TOTP als zweiten Faktor beim Login zu verwenden, scanne den QRCode mit deiner TOTP App.",
"invalid": "TOTP Code ist ungültig",
"keepSession": "2FA (TOTP) für dieses Gerät merken",
"login": "Code verfizieren",
"missing": "Bitte TOTP Code eingeben",
"remove": "2FA (TOTP) deaktivieren"
}
},
"status": {
".": "Status",
"change": "Status aktualisieren",
"hint": "Dein User Status gibt an, wie mit deinen Account Daten umgegangen wird wenn deine Berechtigungen ausgelaufen sind.",
"NORMAL": {
".": "Normal",
"hint": "Dein Account sowie alle gespeicherten Daten werden vier (4) Wochen nach Ablauf gelöscht. Du hast also vier Wochen Zeit deinen Account wieder zu reaktivieren."
},
"PURGE": {
".": "Löschen",
"hint": "Dein Account sowie alle gespeicherten Daten werden umgehend(!) nach Ablauf gelöscht. Du kannst deinen Account nicht(!) mehr reaktivieren."
},
"SLEEP": {
".": "Schlafen",
"hint": "Dein Account sowie alle gespeicherten Daten werden nicht(!) gelöscht. Du kannst deinen Account also jederzeit wieder reaktivieren."
},
"success": "Status erfolgreich geändert"
}
},
"service-unavailable": {
".": "Dienst nicht erreichbar",
"retry": "Seite neu laden",
"support": "Support",
"text": "Zurzeit scheint der Dienst nicht erreichbar zu sein. Wenn diese Meldung länger besteht, melde dich beim Support!"
},
"service": {
"name": "Name",
"text": "Beschreibung"
},
"services": {
".": "Dienste",
"alias_creation": {
"icon": "alternate_email",
"subtitle": "Anlegen von alternativen Namen",
"text": "Du kannst zusätzlich zu deinem Usernamen noch alternative Namen anlegen.",
"title": "Alternative Namen"
},
"empty": "Du hast aktuell keine Berechtigungen zur Nutzung von Diensten.",
"gitea": {
"icon": "code",
"subtitle": "Git-Repositories",
"text": "Alternative zu Diensten wie GitHub, Source Code von bstly-Entwicklungen",
"title": "Gitea"
},
"goto": "Zum Dienst",
"invite_partey": {
"icon": "cake",
"subtitle": "Einladung zur Partey",
"text": "Hier kannst du Einladungen für die Eröffnungs-Partey erstellen.",
"title": "Partey-Einladung"
},
"jitsi": {
"icon": "video_call",
"subtitle": "Video Konferenzen",
"text": "Video Konferenzen mit allen Funktionen für Online-Treffen mit Video und Audio Streams.",
"title": "Jitsi Meet"
},
"mail": {
"icon": "email",
"subtitle": "E-Mail Konto",
"text": "Catch-All an @{username}.we.bstly.de, lernender Spam-Filter und PGP Verschlüsselung.",
"title": "E-Mail Postfach"
},
"matrix": {
"icon": "question_answer",
"subtitle": "Messenger Plattform",
"text": "mit anderen Austauschen, sich Informieren oder einfach quatschen.",
"title": "Matrix"
},
"monitoring": {
"icon": "check",
"subtitle": "System Status",
"text": "Status und Perfomance der aktuellen Systeme",
"title": "Monitoring"
},
"nextcloud": {
"icon": "cloud",
"subtitle": "Cloud Plattform",
"text": "Dateiverwaltung, Kalender, Aufgabenmanagement, Kontaktmanagement, Abstimmungen und mehr.",
"title": "Nextcloud"
},
"owncast": {
"icon": "videocam",
"subtitle": "Livestreams",
"text": "Livestreams von Bastelei e. V.",
"title": "Owncast"
},
"partey": {
"icon": "celebration",
"subtitle": "Virtuelles Vereinsheim",
"text": "Digitaler Treffpunkt für Veranstaltungen oder einfach zum Abhängen.",
"title": "Partey"
},
"registration_vouchers": {
"icon": "card_giftcard",
"subtitle": "Gutschein Code für Registrierungs-Token",
"text": "Einladung um die Dienste des Bastelei e. V. zu nutzen",
"title": "Registrierungs-Gutscheincodes"
},
"ROLE_ADMIN": {
"icon": "admin_panel_settings",
"subtitle": "Administrator von we.bstly",
"text": "Administrator Rechte für die API von we.bstly",
"title": "Administrator"
},
"ROLE_GUEST": {
"icon": "schedule_send",
"subtitle": "Gast Account von we.bstly",
"text": "Gast Account Einladung",
"title": "Gast Account"
},
"ROLE_MEMBER": {
"icon": "loyalty",
"subtitle": "Mitgliedschaft im Bastelei e. V.",
"text": "Reguläres Mitglied im Bastelei e. V.",
"title": "Vereinsmitgliedschaft"
},
"wikijs": {
"icon": "school",
"subtitle": "Informationen, Dokumentation, Anleitungen",
"text": "Alle Information rund um Bastelei e. V. und den angebotenen Diensten, sowie Anleitungen für einzelne Dienste und Funktionen",
"title": "Wiki"
}
},
"software": "Software",
"sourcecode": "Quellcode",
"token": "Token",
"tokens": {
".": "Tokens",
"enter": "Token einlösen",
"get": "Mitgliedschaft",
"invalid": "Das Token ist leider nicht gültig.",
"provide-valid": "Bitte gebe ein gültiges Token ein.",
"redeem": "Tokens einlösen",
"redeemed": "Das Token wurde bereits eingelöst.",
"validate": "Prüfen"
},
"user": {
".": "User",
"aliases": {
".": "Alternative Namen",
"alias": "Alternativer Name",
"confirmDelete": "Möchtest du wirklich den alternativen Namen '{0}' löschen?",
"create": "Alternativen Namen anlegen",
"delete": "Löschen",
"error": "Dieser alternative Name ist leider nicht zulässig.",
"info": "Du kannst hier alternative Namen anlegen. Die Anzahl wird über eine Quota begrenzt.",
"left": "Du kannst noch {0} alternative(n) Namen anlegen.",
"noQuota": "Deine Quota für alternative Namen ist leider aufgebraucht."
},
"domains": {
".": "Domains",
"domain": "Domain",
"confirmDelete": "Möchtest du wirklich die Domain '{0}' löschen?",
"create": "Domain hinzufügen",
"delete": "Löschen",
"error": "Diese Domain ist leider nicht zulässig.",
"info": "Du kannst hier Domains hinzufügen.",
"secret": {
".": "Secret",
"copy": "Secret in die Zwischenablage kopieren",
"copied": "In die Zwischenablage kopiert"
},
"validated": "Validiert"
},
"unavailable": {
".": "Zugriff verweigert",
"text": "Dieser Benutzer existiert nicht oder du hast keine Berechtigungen, um auf das Profil zuzugreifen."
}
},
"username": {
".": "Username",
"error": "Bitte wähle einen anderen Usernamen aus.",
"missing": "Bitte gebe einen Usernamen an."
},
"visibility": {
".": "Sichtbarkeit",
"PRIVATE": {
".": "Privat",
"icon": "lock"
},
"PROTECTED": {
".": "Geschützt",
"icon": "shield"
},
"PUBLIC": {
".": "Öffentlich",
"icon": "public"
}
},
"voucher": {
".": "Gutscheincode",
"code": "Code",
"type": "Typ"
},
"vouchers": {
".": "Gutscheincodes",
"add-on": "Add-On",
"info": "Hier kannst du Gutscheincodes für Add-Ons und Registrierung generieren.",
"registration": "Registrierung",
"stored-safely": {
".": "Da wir keine Verbindungen von Gutscheincodes zu deinem Account speichern, speichere diesen Code bitte selber sicher ab. Falls du die Seite verlässt oder neu lädst ist der Code nicht mehr verfügbar!",
"confirm": "Ich habe den Code sicher abgespeichert!"
},
"temp": {
".": "Temporäre Gutscheincodes",
"info": "Hier werden deine aktuell angefragten Gutscheincodes angezeigt. Bitte speichere diese sicher ab, da wir diese Codes nicht für dich speichern!"
}
}
},
"software": "Software",
"sourcecode": "Quellcode",
"token": "Token",
"tokens": {
".": "Tokens",
"enter": "Token einlösen",
"get": "Mitgliedschaft",
"invalid": "Das Token ist leider nicht gültig.",
"provide-valid": "Bitte gebe ein gültiges Token ein.",
"redeem": "Tokens einlösen",
"redeemed": "Das Token wurde bereits eingelöst.",
"validate": "Prüfen"
},
"user": {
".": "User",
"aliases": {
".": "Alternative Namen",
"alias": "Alternativer Name",
"confirmDelete": "Möchtest du wirklich den alternativen Namen '{0}' löschen?",
"create": "Alternativen Namen anlegen",
"delete": "Löschen",
"error": "Dieser alternative Name ist leider nicht zulässig.",
"info": "Du kannst hier alternative Namen anlegen. Die Anzahl wird über eine Quota begrenzt.",
"left": "Du kannst noch {0} alternative(n) Namen anlegen.",
"noQuota": "Deine Quota für alternative Namen ist leider aufgebraucht."
},
"domains": {
".": "Domains",
"domain": "Domain",
"confirmDelete": "Möchtest du wirklich die Domain '{0}' löschen?",
"create": "Domain hinzufügen",
"delete": "Löschen",
"error": "Diese Domain ist leider nicht zulässig.",
"info": "Du kannst hier Domains hinzufügen.",
"secret": {
".": "Secret",
"copy": "Secret in die Zwischenablage kopieren",
"copied": "In die Zwischenablage kopiert"
},
"validated": "Validiert"
},
"unavailable": {
".": "Zugriff verweigert",
"text": "Dieser Benutzer existiert nicht oder du hast keine Berechtigungen, um auf das Profil zuzugreifen."
}
},
"username": {
".": "Username",
"error": "Bitte wähle einen anderen Usernamen aus.",
"missing": "Bitte gebe einen Usernamen an."
},
"visibility": {
".": "Sichtbarkeit",
"PRIVATE": {
".": "Privat",
"icon": "lock"
},
"PROTECTED": {
".": "Geschützt",
"icon": "shield"
},
"PUBLIC": {
".": "Öffentlich",
"icon": "public"
}
},
"voucher": {
".": "Gutscheincode",
"code": "Code",
"type": "Typ"
},
"vouchers": {
".": "Gutscheincodes",
"add-on": "Add-On",
"info": "Hier kannst du Gutscheincodes für Add-Ons und Registrierung generieren.",
"registration": "Registrierung",
"stored-safely": {
".": "Da wir keine Verbindungen von Gutscheincodes zu deinem Account speichern, speichere diesen Code bitte selber sicher ab. Falls du die Seite verlässt oder neu lädst ist der Code nicht mehr verfügbar!",
"confirm": "Ich habe den Code sicher abgespeichert!"
},
"temp": {
".": "Temporäre Gutscheincodes",
"info": "Hier werden deine aktuell angefragten Gutscheincodes angezeigt. Bitte speichere diese sicher ab, da wir diese Codes nicht für dich speichern!"
}
}
}

View File

@ -1,444 +1,475 @@
{
"account": {
".": "Account",
"advanced": {
".": "Advanced"
}
},
"cancel": "Cancel",
"close": "Close",
"confirm": "Confirm",
"email": {
".": "Email address",
"invalid": "invalid email address",
"primary": {
".": "primary email address",
"hint": "A primary email address is used for contact you instead of you we.bstly address."
}
},
"format": {
"date": "MM/dd/yyy",
"datetime": "MM/dd/yyy h:mm a",
"time": "h:mm:ss a"
},
"greet": "Hello {0}",
"help": "Help",
"imprint": "Imprint",
"info": {
".": "Info"
},
"jitsi": {
"rooms": {
".": "Jitsi Rooms",
"confirmDelete": "Are you sure you want to delete your Jitsi Room '{0}'?",
"create": "Create Jitsi Room",
"delete": "Delete",
"error": {
"expires": "Invalid expiry.",
"moderationStarts" : "Invalid moderation starts. Moderation have to start before conference starts",
"room": "Please choose a different name. The name is already taken or contains invalid characters. Only letters and numbers are allowed.",
"starts": "Invalid start."
},
"expires": "Expires",
"info": "You can create new Jitsi Rooms here. The number is limited due to a quota.",
"left": "You have {0} Jitsi Room(s) left.",
"moderationStarts" : "Moderation starts",
"moderationUrl": "Moderation url",
"noQuota": "Your quota for Jitsi Rooms is depleted.",
"notStarted" : "The conference has not started yet.",
"room": "Name",
"starts": "Starts"
"account": {
".": "Account",
"advanced": {
".": "Advanced"
}
},
"share": {
".": "Share",
"clipboard": {
"copied": "Copied to clipboard",
"text": "Copy text to clipboard",
"url": "Copy url to clipboard"
},
"email": {
".": "Share via email",
"subject": "Invite to video conference {0}"
},
"text": {
"both": "The conference begins at {0} and ends at {1}.",
"expires": "The conference ends at {0}.",
"intro": "Hello, I want to invite you to a video conference.",
"outro": "You can join the conference under:\n{0}",
"starts": "The conference begins at {0}."
}
}
},
"locale": {
"de-informal": {
"long": "Deutsch",
"short": "DE"
"cancel": "Cancel",
"close": "Close",
"confirm": "Confirm",
"email": {
".": "Email address",
"invalid": "invalid email address",
"primary": {
".": "primary email address",
"hint": "A primary email address is used for contact you instead of you we.bstly address."
}
},
"en": {
"long": "English",
"short": "EN"
}
},
"login": {
".": "Login",
"external": "Login",
"invalid": "Wrong username or password.",
"keepSession": "Stay logged in"
},
"logout": "Logout",
"not-found": {
".": "Nothing found",
"text": "This page was not found."
},
"ok": "Ok",
"password": {
".": "Password",
"change": "Change password",
"changed": "Successfully changed password",
"confirm": "Confirm password",
"current": "Current password",
"error": {
"ILLEGAL_WHITESPACE": "Please don't use space characters.",
"INSUFFICIENT_DIGIT": "Please enter at least one digit.",
"INSUFFICIENT_LOWERCASE": "Please enter at least one lower case character.",
"INSUFFICIENT_SPECIAL": "Please enter at least one special character.",
"INSUFFICIENT_UPPERCASE": "Please enter at least one upper case character.",
"TOO_SHORT": "Please choose a longer password."
"format": {
"date": "MM/dd/yyy",
"datetime": "MM/dd/yyy h:mm a",
"time": "h:mm:ss a"
},
"forgot": "Forgot password",
"invalid": {
"hint": "Please provide password in a valid format."
"greet": "Hello {0}",
"help": "Help",
"imprint": "Imprint",
"info": {
".": "Info"
},
"not-match": "Passwords did not match.",
"request": "Request new password",
"reset": {
".": "Set password",
"login": "Back to login",
"success": {
"text": "Your new password has been applied. You can now login with your new password.",
"title": "Successfully changed password"
}
}
},
"permissions": {
".": "Permissions",
"expires": "Valid until",
"name": "Name",
"starts": "Valid from"
},
"pgp": {
".": "PGP",
"privateKey": {
".": "Private PGP key",
"confirmStore": "I have securely stored my private key!",
"downloadKey": "Download private key",
"help": "Currently your private key will be used for password reset. Besides it can be used to decrypt emails and authenticate against certain services.",
"downloaded": "Stored private key otherwise.",
"copyKey": "Copy to clipboard"
}
},
"privacy-policy": "Privacy Policy",
"profile": "Profile",
"profileField": {
".": "Profile field",
"confirmDelete": "Are you sure you want to delete your profile field '{0}'?",
"create": "Add new profile field",
"delete": "Delete",
"edit": "Edit",
"error": {
"BLOB": {
".": "Invalid binary blob"
},
"BOOL": {
".": "Invalid boolean"
},
"DATE": {
".": "Invalid date"
},
"EMAIL": {
".": "Invalid email address"
},
"name": "name invalid or too short",
"NUMBER": {
".": "Invalid numeric"
},
"TEXT": {
".": "Text too long"
},
"type": "Invalid type for this profile field",
"URL": {
".": "Invalid URL"
},
"visibility": "Invalid visibility for profile field"
"invite": {
".": "Invite",
"create": "Create Invite",
"expires": "Expires",
"link": "Link",
"note": "Note",
"starts": "Starts"
},
"index": "Index",
"name": {
".": "Key",
"darkTheme": "Dark Theme",
"email": "Email address",
"locale": "Locale",
"primaryEmail": "Use email address primary",
"prtyMap": "Partey map",
"publicKey": "Public PGP key"
"invites": {
".": "Invites",
"info": "You can create new invites here. To edit an invite like adding a note or change the personal invite message just click on the invite link. If you are authorized, you can change the texts directly on the invite page.",
"left": "You have {0} invites left.",
"others": "Other's invites",
"search": "Search"
},
"openBlob": "Display",
"type": {
".": "Type",
"BLOB": {
".": "Binary blob"
},
"BOOL": {
".": "Boolean"
},
"DATE": {
".": "Date"
},
"EMAIL": {
".": "Email"
},
"NUMBER": {
".": "Numeric"
},
"TEXT": {
".": "Text"
},
"URL": {
".": "URL"
}
},
"value": "Value"
},
"quotas": {
".": "Quotas",
"name": "Name",
"unit": {
"#": "# (Count)",
".": "Unit",
"G": "GB (Gigabyte)"
},
"value": "Quota"
},
"register": {
".": "Registration",
"login": "To Login",
"success": {
"text": "Successfully registered your Account. You can login now!",
"title": "Finished registration"
},
"token.missing": "You need a valid token!"
},
"save": "Save",
"security": {
".": "Security",
"2fa": {
".": "Two-Factor-Authentication (2FA)",
"info": "You can additionally add a second factor to your password. Please keep in mind, that this only affects your we.bstly-Account and not your email login. Currently only TOTP (also known as Google Authenticator) is supported as 2FA method.",
"totp": {
".": "2FA (TOTP)",
"activate": "Please enter your current code to enable TOTP as your 2FA.",
"code": "TOTP code",
"create": "2FA (TOTP) create",
"enable": "Enable 2FA (TOTP)",
"external": "2FA (TOTP)",
"hint": "To use TOP as second factor, please scan the shown QR-Code with your TOTP App.",
"invalid": "Invalid TOTP code",
"keepSession": "Remember 2FA (TOTP) for this device",
"login": "Verify code",
"missing": "Please enter TOTP code",
"remove": "Disable 2FA (TOTP)"
}
},
"status": {
".": "Status",
"change": "Change status",
"hint": "You Account Status controls the handling of your account data after all permissions expired.",
"NORMAL": {
".": "Normal",
"hint": "Your account and all your data will be deleted after four (4) weeks. So you have 4 weeks left to reactivate your account."
},
"PURGE": {
".": "Purge",
"hint": "Your account and all your data will be deleted immediately after expiry. So you can not(!) reactivate your account."
},
"SLEEP": {
".": "Sleep",
"hint": "Your account and all your data will not(!) be deleted. So you have reactivate your account anytime."
},
"success": "Status successfully changed"
}
},
"service-unavailable": {
".": "Service unavailable",
"retry": "Reload page",
"support": "Support",
"text": "The service seems currently unavailable. If this message appears over a longer period, please contact our support!"
},
"services": {
".": "Services",
"alias_creation": {
"icon": "alternate_email",
"subtitle": "Creation of Aliases",
"text": "You can add additional aliases besides your username.",
"title": "Aliases"
},
"empty": "You have insufficient permissions to use any services.",
"gitea": {
"icon": "code",
"subtitle": "Git-Repositories",
"text": "Alternative of services like GitHub, Source Code of bstly-development",
"title": "Gitea"
},
"goto": "To service",
"jitsi": {
"icon": "video_call",
"subtitle": "Video conferencing",
"text": "Video conferencing with all functionality needed for meeting online with Video and Audio streams.",
"title": "Jitsi Meet"
"rooms": {
".": "Jitsi Rooms",
"confirmDelete": "Are you sure you want to delete your Jitsi Room '{0}'?",
"create": "Create Jitsi Room",
"delete": "Delete",
"error": {
"expires": "Invalid expiry.",
"moderationStarts": "Invalid moderation starts. Moderation have to start before conference starts",
"room": "Please choose a different name. The name is already taken or contains invalid characters. Only letters and numbers are allowed.",
"starts": "Invalid start."
},
"expires": "Expires",
"info": "You can create new Jitsi Rooms here. The number is limited due to a quota.",
"left": "You have {0} Jitsi Room(s) left.",
"moderationStarts": "Moderation starts",
"moderationUrl": "Moderation url",
"noQuota": "Your quota for Jitsi Rooms is depleted.",
"notStarted": "The conference has not started yet.",
"room": "Name",
"starts": "Starts"
},
"share": {
".": "Share",
"clipboard": {
"copied": "Copied to clipboard",
"text": "Copy text to clipboard",
"url": "Copy url to clipboard"
},
"email": {
".": "Share via email",
"subject": "Invite to video conference {0}"
},
"text": {
"both": "The conference begins at {0} and ends at {1}.",
"expires": "The conference ends at {0}.",
"intro": "Hello, I want to invite you to a video conference.",
"outro": "You can join the conference under:\n{0}",
"starts": "The conference begins at {0}."
}
}
},
"mail": {
"icon": "email",
"subtitle": "Email Account",
"text": "Catch-All to @{username}.we.bstly.de, learning spam-filter and PGP encryption.",
"title": "Email Account"
"locale": {
"de-informal": {
"long": "Deutsch",
"short": "DE"
},
"en": {
"long": "English",
"short": "EN"
}
},
"matrix": {
"icon": "question_answer",
"subtitle": "Messenger",
"text": "talk, exchange, discuss with others",
"title": "Matrix"
"login": {
".": "Login",
"external": "Login",
"invalid": "Wrong username or password.",
"keepSession": "Stay logged in"
},
"nextcloud": {
"icon": "cloud",
"subtitle": "Cloud service",
"text": "File management, calendar, tasks, contacts, polls and more.",
"title": "Nextcloud"
"logout": "Logout",
"not-found": {
".": "Nothing found",
"text": "This page was not found."
},
"owncast": {
"icon": "videocam",
"subtitle": "Livestreams",
"text": "Livestreams of Bastelei e. V.",
"title": "Owncast"
"ok": "Ok",
"password": {
".": "Password",
"change": "Change password",
"changed": "Successfully changed password",
"confirm": "Confirm password",
"current": "Current password",
"error": {
"ILLEGAL_WHITESPACE": "Please don't use space characters.",
"INSUFFICIENT_DIGIT": "Please enter at least one digit.",
"INSUFFICIENT_LOWERCASE": "Please enter at least one lower case character.",
"INSUFFICIENT_SPECIAL": "Please enter at least one special character.",
"INSUFFICIENT_UPPERCASE": "Please enter at least one upper case character.",
"TOO_SHORT": "Please choose a longer password."
},
"forgot": "Forgot password",
"invalid": {
"hint": "Please provide password in a valid format."
},
"not-match": "Passwords did not match.",
"request": "Request new password",
"reset": {
".": "Set password",
"login": "Back to login",
"success": {
"text": "Your new password has been applied. You can now login with your new password.",
"title": "Successfully changed password"
}
}
},
"partey": {
"icon": "celebration",
"subtitle": "Virtual clubhouse",
"text": "Digital place for events or hang-outs.",
"title": "Partey"
"permissions": {
".": "Permissions",
"expires": "Valid until",
"name": "Name",
"starts": "Valid from"
},
"registration_vouchers": {
"icon": "card_giftcard",
"subtitle": "Voucher for Registration-Token",
"text": "Invite to use the services of Bastelei e. V.",
"title": "Registration vouchers"
"pgp": {
".": "PGP",
"privateKey": {
".": "Private PGP key",
"confirmStore": "I have securely stored my private key!",
"downloadKey": "Download private key",
"help": "Currently your private key will be used for password reset. Besides it can be used to decrypt emails and authenticate against certain services.",
"downloaded": "Stored private key otherwise.",
"copyKey": "Copy to clipboard"
}
},
"ROLE_ADMIN": {
"icon": "admin_panel_settings",
"subtitle": "Administrator of we.bstly",
"text": "Administrator privileges for we.bstly API",
"title": "Administrator"
"privacy-policy": "Privacy Policy",
"profile": "Profile",
"profileField": {
".": "Profile field",
"confirmDelete": "Are you sure you want to delete your profile field '{0}'?",
"create": "Add new profile field",
"delete": "Delete",
"edit": "Edit",
"error": {
"BLOB": {
".": "Invalid binary blob"
},
"BOOL": {
".": "Invalid boolean"
},
"DATE": {
".": "Invalid date"
},
"EMAIL": {
".": "Invalid email address"
},
"name": "name invalid or too short",
"NUMBER": {
".": "Invalid numeric"
},
"TEXT": {
".": "Text too long"
},
"type": "Invalid type for this profile field",
"URL": {
".": "Invalid URL"
},
"visibility": "Invalid visibility for profile field"
},
"index": "Index",
"name": {
".": "Key",
"darkTheme": "Dark Theme",
"email": "Email address",
"locale": "Locale",
"primaryEmail": "Use email address primary",
"prtyMap": "Partey map",
"publicKey": "Public PGP key"
},
"openBlob": "Display",
"type": {
".": "Type",
"BLOB": {
".": "Binary blob"
},
"BOOL": {
".": "Boolean"
},
"DATE": {
".": "Date"
},
"EMAIL": {
".": "Email"
},
"NUMBER": {
".": "Numeric"
},
"TEXT": {
".": "Text"
},
"URL": {
".": "URL"
}
},
"value": "Value"
},
"ROLE_GUEST": {
"icon": "schedule_send",
"subtitle": "Guest Account of we.bstly",
"text": "Guest Account Invite",
"title": "Guest Account"
"quotas": {
".": "Quotas",
"name": "Name",
"unit": {
"#": "# (Count)",
".": "Unit",
"G": "GB (Gigabyte)"
},
"value": "Quota"
},
"ROLE_MEMBER": {
"icon": "loyalty",
"subtitle": "Membership of Bastelei e. V.",
"text": "Valid member of Bastelei e. V.",
"title": "Club membership"
"register": {
".": "Registration",
"login": "To Login",
"success": {
"text": "Successfully registered your Account. You can login now!",
"title": "Finished registration"
},
"token.missing": "You need a valid token!"
},
"wikijs": {
"icon": "school",
"subtitle": "Informationen, documentation, guides",
"text": "All informations about Bastelei e. V. and provides service, together with guides for individual services and functions.",
"title": "Wiki"
"save": "Save",
"security": {
".": "Security",
"2fa": {
".": "Two-Factor-Authentication (2FA)",
"info": "You can additionally add a second factor to your password. Please keep in mind, that this only affects your we.bstly-Account and not your email login. Currently only TOTP (also known as Google Authenticator) is supported as 2FA method.",
"totp": {
".": "2FA (TOTP)",
"activate": "Please enter your current code to enable TOTP as your 2FA.",
"code": "TOTP code",
"create": "2FA (TOTP) create",
"enable": "Enable 2FA (TOTP)",
"external": "2FA (TOTP)",
"hint": "To use TOP as second factor, please scan the shown QR-Code with your TOTP App.",
"invalid": "Invalid TOTP code",
"keepSession": "Remember 2FA (TOTP) for this device",
"login": "Verify code",
"missing": "Please enter TOTP code",
"remove": "Disable 2FA (TOTP)"
}
},
"status": {
".": "Status",
"change": "Change status",
"hint": "You Account Status controls the handling of your account data after all permissions expired.",
"NORMAL": {
".": "Normal",
"hint": "Your account and all your data will be deleted after four (4) weeks. So you have 4 weeks left to reactivate your account."
},
"PURGE": {
".": "Purge",
"hint": "Your account and all your data will be deleted immediately after expiry. So you can not(!) reactivate your account."
},
"SLEEP": {
".": "Sleep",
"hint": "Your account and all your data will not(!) be deleted. So you have reactivate your account anytime."
},
"success": "Status successfully changed"
}
},
"service-unavailable": {
".": "Service unavailable",
"retry": "Reload page",
"support": "Support",
"text": "The service seems currently unavailable. If this message appears over a longer period, please contact our support!"
},
"service": {
"name": "Name",
"text": "Description"
},
"services": {
".": "Services",
"alias_creation": {
"icon": "alternate_email",
"subtitle": "Creation of Aliases",
"text": "You can add additional aliases besides your username.",
"title": "Aliases"
},
"empty": "You have insufficient permissions to use any services.",
"gitea": {
"icon": "code",
"subtitle": "Git-Repositories",
"text": "Alternative of services like GitHub, Source Code of bstly-development",
"title": "Gitea"
},
"goto": "To service",
"invite_partey": {
"icon": "cake",
"subtitle": "Invite to Partey",
"text": "Create Invites for Opening Partey.",
"title": "Partey-Invites"
},
"jitsi": {
"icon": "video_call",
"subtitle": "Video conferencing",
"text": "Video conferencing with all functionality needed for meeting online with Video and Audio streams.",
"title": "Jitsi Meet"
},
"mail": {
"icon": "email",
"subtitle": "Email Account",
"text": "Catch-All to @{username}.we.bstly.de, learning spam-filter and PGP encryption.",
"title": "Email Account"
},
"matrix": {
"icon": "question_answer",
"subtitle": "Messenger",
"text": "talk, exchange, discuss with others",
"title": "Matrix"
},
"monitoring": {
"icon": "check",
"subtitle": "System Status",
"text": "Status and perfomance of current systems",
"title": "Monitoring"
},
"nextcloud": {
"icon": "cloud",
"subtitle": "Cloud service",
"text": "File management, calendar, tasks, contacts, polls and more.",
"title": "Nextcloud"
},
"owncast": {
"icon": "videocam",
"subtitle": "Livestreams",
"text": "Livestreams of Bastelei e. V.",
"title": "Owncast"
},
"partey": {
"icon": "celebration",
"subtitle": "Virtual clubhouse",
"text": "Digital place for events or hang-outs.",
"title": "Partey"
},
"registration_vouchers": {
"icon": "card_giftcard",
"subtitle": "Voucher for Registration-Token",
"text": "Invite to use the services of Bastelei e. V.",
"title": "Registration vouchers"
},
"ROLE_ADMIN": {
"icon": "admin_panel_settings",
"subtitle": "Administrator of we.bstly",
"text": "Administrator privileges for we.bstly API",
"title": "Administrator"
},
"ROLE_GUEST": {
"icon": "schedule_send",
"subtitle": "Guest Account of we.bstly",
"text": "Guest Account Invite",
"title": "Guest Account"
},
"ROLE_MEMBER": {
"icon": "loyalty",
"subtitle": "Membership of Bastelei e. V.",
"text": "Valid member of Bastelei e. V.",
"title": "Club membership"
},
"wikijs": {
"icon": "school",
"subtitle": "Informationen, documentation, guides",
"text": "All informations about Bastelei e. V. and provides service, together with guides for individual services and functions.",
"title": "Wiki"
}
},
"software": "Software",
"sourcecode": "Source Code",
"token": "Token",
"tokens": {
".": "Token",
"enter": "Enter token",
"get": "Membership",
"invalid": "The provided token is invalid.",
"provide-valid": "Please provide a valid token.",
"redeem": "Redeem token",
"redeemed": "The provided token has already been redeemed.",
"validate": "Validate"
},
"user": {
".": "User",
"aliases": {
".": "Aliases",
"alias": "Alias",
"confirmDelete": "Are you sure you want to delete your alias '{0}'?",
"create": "Add Alias",
"delete": "Delete",
"error": "This alias is invalid.",
"info": "You can add Aliases here. The number is limited due to a quota.",
"left": "You have {0} Alias(es) left.",
"noQuota": "Your quota for Aliases is depleted."
},
"domains": {
".": "Domains",
"domain": "Domain",
"confirmDelete": "Are you sure you want to delete your domain '{0}'?",
"create": "Add Domain",
"delete": "Delete",
"error": "This domain is invalid.",
"info": "You can add Domains here.",
"secret": {
".": "Secret",
"copy": "Copy secret to clipboard",
"copied": "Copied to clipboard"
},
"validated": "Validated"
},
"unavailable": {
".": "Access denied",
"text": "The provided user does not exists or you have insufficient privileges to access profile."
}
},
"username": {
".": "Username",
"error": "Please choose a different username.",
"missing": "Please enter a valid username."
},
"visibility": {
".": "Visibility",
"PRIVATE": {
".": "Private",
"icon": "lock"
},
"PROTECTED": {
".": "Protected",
"icon": "shield"
},
"PUBLIC": {
".": "Public",
"icon": "public"
}
},
"voucher": {
".": "Voucher",
"code": "Code",
"type": "Typ"
},
"vouchers": {
".": "Vouchers",
"add-on": "Add-On",
"info": "Generate vouchers for Add-Ons and registration.",
"registration": "Registration",
"stored-safely": {
".": "Please store the provided voucher code securely, since we do not store any relation of vouchers to your account. If you leave page or reload the code won't be shown up again!",
"confirm": "I securely stored the given voucher!"
},
"temp": {
".": "Temporary vouchers",
"info": "You currently requested vouchers are displayed here. Please store them securely!"
}
}
},
"software": "Software",
"sourcecode": "Source Code",
"token": "Token",
"tokens": {
".": "Token",
"enter": "Enter token",
"get": "Membership",
"invalid": "The provided token is invalid.",
"provide-valid": "Please provide a valid token.",
"redeem": "Redeem token",
"redeemed": "The provided token has already been redeemed.",
"validate": "Validate"
},
"user": {
".": "User",
"aliases": {
".": "Aliases",
"alias": "Alias",
"confirmDelete": "Are you sure you want to delete your alias '{0}'?",
"create": "Add Alias",
"delete": "Delete",
"error": "This alias is invalid.",
"info": "You can add Aliases here. The number is limited due to a quota.",
"left": "You have {0} Alias(es) left.",
"noQuota": "Your quota for Aliases is depleted."
},
"domains": {
".": "Domains",
"domain": "Domain",
"confirmDelete": "Are you sure you want to delete your domain '{0}'?",
"create": "Add Domain",
"delete": "Delete",
"error": "This domain is invalid.",
"info": "You can add Domains here.",
"secret": {
".": "Secret",
"copy": "Copy secret to clipboard",
"copied": "Copied to clipboard"
},
"validated": "Validated"
},
"unavailable": {
".": "Access denied",
"text": "The provided user does not exists or you have insufficient privileges to access profile."
}
},
"username": {
".": "Username",
"error": "Please choose a different username.",
"missing": "Please enter a valid username."
},
"visibility": {
".": "Visibility",
"PRIVATE": {
".": "Private",
"icon": "lock"
},
"PROTECTED": {
".": "Protected",
"icon": "shield"
},
"PUBLIC": {
".": "Public",
"icon": "public"
}
},
"voucher": {
".": "Voucher",
"code": "Code",
"type": "Typ"
},
"vouchers": {
".": "Vouchers",
"add-on": "Add-On",
"info": "Generate vouchers for Add-Ons and registration.",
"registration": "Registration",
"stored-safely": {
".": "Please store the provided voucher code securely, since we do not store any relation of vouchers to your account. If you leave page or reload the code won't be shown up again!",
"confirm": "I securely stored the given voucher!"
},
"temp": {
".": "Temporary vouchers",
"info": "You currently requested vouchers are displayed here. Please store them securely!"
}
}
}