Compare commits

..

4 Commits

21 changed files with 379 additions and 50 deletions

View File

@ -57,7 +57,7 @@ users.route('/')
return next();
}
UserModel.count(dbFilter, (err, totalCount) => {
UserModel.countDocuments(dbFilter, (err, totalCount) => {
res.set('x-total-count', totalCount);
res.locals.items = users;
res.locals.processed = true;

View File

@ -89,6 +89,21 @@
</ul>
<ul class="nav navbar-nav pull-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
Language
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>
<a (click)="setLanguage('en')">English</a>
</li>
<li>
<a (click)="setLanguage('de')">Deutsch</a>
</li>
</ul>
</li>
<li *ngIf="loginService.hasPermission(4)" routerLinkActive="active">
<a routerLink='{{config.adminPanelPath}}' class="link">{{'navigation.top.admin' | translate}}</a>
</li>

View File

@ -9,6 +9,7 @@ import {DomSanitizer} from '@angular/platform-browser';
import {MatIconRegistry} from '@angular/material';
import {SpinnerService} from './services/user-interface/spinner/spinner.service';
import {TranslateService} from '@ngx-translate/core';
import {SettingsService} from './services/settings.service';
declare function require(url: string);
@ -66,6 +67,7 @@ export class AppComponent implements OnInit {
private sanitizer: DomSanitizer,
private spinnerService: SpinnerService,
private translate: TranslateService,
private settingsService: SettingsService,
@Inject(DOCUMENT) private document) {
this.initMaterialSvgIcons();
@ -88,7 +90,7 @@ export class AppComponent implements OnInit {
}
ngOnInit() {
this.translate.setDefaultLang('de');
this.settingsService.getLanguage().subscribe((language) => this.translate.setDefaultLang(language));
if (this.loginService.hasPermission(2)) {
const fraction = this.loginService.getCurrentUser().squad.fraction;
this.promotionService.checkUnconfirmedPromotions(fraction);
@ -126,12 +128,10 @@ export class AppComponent implements OnInit {
this.document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
}
// TODO: use!
// private setLanguage(settings: SettingsState) {
// const { language } = settings;
// if (language) {
// this.translate.use(language);
// }
// }
setLanguage(language: string) {
if (language) {
this.settingsService.setLanguage(language);
}
}
}

View File

@ -26,6 +26,7 @@ import {HttpClientModule} from '@angular/common/http';
import {SpinnerService} from './services/user-interface/spinner/spinner.service';
import {MatSnackBarModule} from '@angular/material';
import {HttpClient} from './services/http-client';
import {SettingsService} from './services/settings.service';
@NgModule({
imports: [
@ -61,6 +62,7 @@ import {HttpClient} from './services/http-client';
CookieService,
SnackBarService,
SpinnerService,
SettingsService,
],
declarations: [

View File

@ -1,6 +1,7 @@
import {Component, Input, OnInit} from '@angular/core';
import {FormGroup, NgForm} from '@angular/forms';
import {TranslateService} from '@ngx-translate/core';
import {SettingsService} from '../../services/settings.service';
@Component({
selector: 'show-error',
@ -11,7 +12,7 @@ import {TranslateService} from '@ngx-translate/core';
</div>
</div>`
})
export class ShowErrorComponent implements OnInit {
export class ShowErrorComponent {
// tslint:disable-next-line:no-input-rename
@Input('controlPath') controlPath;
@ -22,12 +23,10 @@ export class ShowErrorComponent implements OnInit {
private form: FormGroup;
constructor(ngForm: NgForm,
private translate: TranslateService) {
private translate: TranslateService,
private settingsService: SettingsService) {
this.form = ngForm.form;
}
ngOnInit() {
this.translate.setDefaultLang('de');
this.settingsService.getLanguage().subscribe((language) => this.translate.setDefaultLang(language));
}
get errorMessages(): string[] {

View File

@ -1,16 +1,19 @@
<form class="form-signin" (ngSubmit)="login(userName.value, password.value)">
<div class="row">
<h2 class="form-signin-heading">Login</h2>
<h2 class="form-signin-heading">{{'login.headline' | translate}}</h2>
<label for="inputEmail" class="sr-only">Benutzername</label>
<input #userName id="inputEmail" class="form-control" placeholder="Benutzername" required="" autofocus="">
<label for="inputEmail" class="sr-only">{{'login.username' | translate}}</label>
<input #userName id="inputEmail" class="form-control"
placeholder="{{'login.username' | translate}}"
required="" autofocus="">
<label for="inputPassword" class="sr-only">Passwort</label>
<input #password type="password" id="inputPassword" class="form-control" placeholder="Passwort" required="">
<label for="inputPassword" class="sr-only">{{'login.password' | translate}}</label>
<input #password type="password" id="inputPassword" class="form-control"
placeholder="{{'login.password' | translate}}" required="">
<div class="form-group">
<button mat-stroked-button type="submit">
<span *ngIf="!loading">Anmelden</span>
<span *ngIf="!loading">{{'login.submit' | translate}}</span>
<span *ngIf="loading" class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></span>
</button>
</div>

View File

@ -1,5 +1,6 @@
import {Component} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {SettingsService} from '../services/settings.service';
@Component({
selector: 'cc-manage-root',
@ -7,7 +8,8 @@ import {TranslateService} from '@ngx-translate/core';
styleUrls: ['./manage.component.scss']
})
export class ManageComponent {
constructor(private translate: TranslateService) {
this.translate.setDefaultLang('de');
constructor(private translate: TranslateService,
private settingsService: SettingsService) {
this.settingsService.getLanguage().subscribe((language) => this.translate.setDefaultLang(language));
}
}

View File

@ -1,5 +1,6 @@
import {Component} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {SettingsService} from '../services/settings.service';
@Component({
selector: 'cc-public',
@ -7,7 +8,8 @@ import {TranslateService} from '@ngx-translate/core';
styleUrls: ['./public.component.scss']
})
export class PublicComponent {
constructor(private translate: TranslateService) {
this.translate.setDefaultLang('de');
constructor(private translate: TranslateService,
private settingsService: SettingsService) {
this.settingsService.getLanguage().subscribe((language) => this.translate.setDefaultLang(language));
}
}

View File

@ -8,7 +8,7 @@
<th class="col-sm-1">{{'request.confirm.promotion.table.head.participant' | translate}}</th>
<th class="col-sm-1">{{'request.confirm.promotion.table.head.rank.before' | translate}}</th>
<th class="col-sm-1">{{'request.confirm.promotion.table.head.rank.after' | translate}}</th>
<th class="col-sm-1 ">{{'"request.confirm.promotion.table.head.requester' | translate}}</th>
<th class="col-sm-1 ">{{'request.confirm.promotion.table.head.requester' | translate}}</th>
<th class="col-sm-1 text-center">{{'request.confirm.promotion.table.head.date' | translate}}</th>
<th class="col-sm-1 text-center">{{'request.confirm.promotion.table.head.status' | translate}}</th>
<th class="col-sm-2 text-right"></th>

View File

@ -1,5 +1,6 @@
import {Component} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {SettingsService} from '../services/settings.service';
@Component({
selector: 'cc-request-root',
@ -7,7 +8,8 @@ import {TranslateService} from '@ngx-translate/core';
styleUrls: ['request.component.css']
})
export class RequestComponent {
constructor(private translate: TranslateService) {
translate.setDefaultLang('de');
constructor(private translate: TranslateService,
private settingsService: SettingsService) {
this.settingsService.getLanguage().subscribe((language) => this.translate.setDefaultLang(language));
}
}

View File

@ -0,0 +1,21 @@
import {Injectable} from '@angular/core';
import {BehaviorSubject} from 'rxjs';
import {Observable} from 'rxjs/internal/Observable';
@Injectable()
export class SettingsService {
language = new BehaviorSubject<string>('de');
constructor() {
}
setLanguage(language: string) {
this.language.next(language);
}
getLanguage(): Observable<string> {
return this.language.asObservable();
}
}

View File

@ -26,7 +26,7 @@
[legendTitle]="legendTitle"
[showXAxisLabel]="showXAxisLabel"
[showYAxisLabel]="showYAxisLabel"
[yAxisLabel]="yAxisLabel"
yAxisLabel="{{yAxisLabel | translate}}"
[autoScale]="autoscale"
[timeline]="timeline"
[roundDomains]="roundDomains">

View File

@ -66,19 +66,6 @@ export class StatisticOverviewComponent implements OnInit {
if (itemsProcessed === campaigns.length) {
if (this.id === 'all') {
this.timeline = true;
this.title = 'Gesamtübersicht';
wars.sort((a, b) => {
// sort by dates, because older campaign can contain newer war
if (a['date'] > (b['date'])) {
return -1;
}
if (a['date'] < (b['date'])) {
return 1;
}
return 0;
});
} else {
this.title = campaign.title;
}
this.initChart(wars);
}
@ -89,15 +76,15 @@ export class StatisticOverviewComponent implements OnInit {
switch (parseInt(index, 10)) {
case 0:
this.currentData = this.pointSumData;
this.yAxisLabel = 'Gesamtpunkte';
this.yAxisLabel = 'stats.graph.select.sum.points';
break;
case 1:
this.currentData = this.pointData;
this.yAxisLabel = 'Punkte';
this.yAxisLabel = 'stats.graph.select.battle.points';
break;
case 2:
this.currentData = this.playerData;
this.yAxisLabel = 'Anzahl Spieler';
this.yAxisLabel = 'stats.graph.select.player.count';
break;
}
}

View File

@ -3,6 +3,7 @@ import {Campaign} from '../models/model-interfaces';
import {CampaignService} from '../services/logs/campaign.service';
import {ActivatedRoute, Router} from '@angular/router';
import {TranslateService} from '@ngx-translate/core';
import {SettingsService} from '../services/settings.service';
@Component({
selector: 'cc-stats',
@ -20,8 +21,9 @@ export class StatisticComponent implements OnInit {
constructor(private campaignService: CampaignService,
private router: Router,
private route: ActivatedRoute,
private translate: TranslateService) {
this.translate.setDefaultLang('de');
private translate: TranslateService,
private settingsService: SettingsService) {
this.settingsService.getLanguage().subscribe((language) => this.translate.setDefaultLang(language));
}
ngOnInit() {

View File

@ -11,7 +11,7 @@
"navigation.top.board": "Zum Forum",
"navigation.top.overview": "Armeeübersicht",
"navigation.top.ranks": "Ränge",
"navigation.top.decorations": "Auszeichnungem",
"navigation.top.decorations": "Auszeichnungen",
"navigation.top.statistics": "Statistiken",
"navigation.top.login": "Login",
"navigation.top.logout": "Abmelden",

View File

@ -1,2 +1,52 @@
{
"public.error.message.required": "{{fieldName}} is a required field",
"public.error.message.min.length": "{{fieldName}} requires at least {{boundary}} characters",
"public.error.message.max.length": "{{fieldName}} is limited to {{boundary}} characters",
"public.error.message.email": "Please enter a valid eMail address",
"public.error.message.no.user": "The choosen user does not exist",
"public.error.message.default": "{{fieldName}} is not valid",
"public.common.search.button": "Search",
"navigation.top.board": "To message board",
"navigation.top.overview": "Army Overview",
"navigation.top.ranks": "Ranks",
"navigation.top.decorations": "Awards",
"navigation.top.statistics": "Statistics",
"navigation.top.login": "Login",
"navigation.top.logout": "Logout",
"navigation.top.management": "Manage",
"navigation.top.management.users": "Users",
"navigation.top.management.squads": "Squads",
"navigation.top.management.decorations": "Decorations",
"navigation.top.management.ranks": "Ranks",
"navigation.top.request": "Request",
"navigation.top.request.open": "Open Requests",
"navigation.top.request.manage": "Requests",
"navigation.top.request.promotion": "Promotion",
"navigation.top.request.award": "Medal/ Ribbon",
"navigation.top.admin": "Admin Panel",
"navigation.button.scroll.top": "Back to top",
"login.headline": "Login",
"login.username": "Username",
"login.password": "Password",
"login.submit": "Submit",
"signup.headline": "Sign Up",
"signup.username": "Username",
"signup.password": "Password",
"signup.secret": "Secret",
"signup.secret.placeholder": "Secret text for message comparison",
"signup.submit": "Submit",
"signup.description": "Dieses Formular nur ausfüllen wenn du einer <b>HL</b> angehörst oder <b>SQL</b> bist. Dabei den Nutzernamen aus dem OPT Forum verwenden! Im Forum eine Nachricht an <a href=\"https://www.opt4.net/dashboard/index.php?conversation-add/&userID=9\" target=\"_blank\">HardiReady</a> senden, in welcher der 'geheime Text' steht, den du bei der Registrierung nutzt.<br> Dabei kann es sich um irgend eine willkürliche Zeichenfolge oder einen Satz handeln - dient nur dem Abgleich. Anschließend wird dein Account aktiviert und du wirst darüber per PN informiert.",
"public.army.headline": "Overview of all players, squads and armies",
"public army.squad.members": "Members:",
"public.army.members": "Army Members:",
"public.army.member.button.back": "Back",
"public.army.member.button.copy": "copy",
"public.army.member.headline": "Awards of {{name}}",
"public.army.member.awards.title": "Title",
"public.army.member.awards.reason": "Reason",
"public.army.member.awards.date": "Decorated at"
}

View File

@ -1,3 +1,94 @@
{
"public.error.message.required": "{{fieldName}} is a required field",
"public.error.message.min.length": "{{fieldName}} requires at least {{boundary}} characters",
"public.error.message.max.length": "{{fieldName}} is limited to {{boundary}} characters",
"public.error.message.email": "Please enter a valid eMail address",
"public.error.message.no.user": "The choosen user does not exist",
"public.error.message.default": "{{fieldName}} is not valid",
"public.common.search.button": "Search",
"decorations.list.button.add": "Add new decoration",
"decorations.list.button.delete": "Delete",
"decorations.list.filter.global": "Global",
"decorations.list.delete.confirm": "Do you really want to delete the decoration '{{name}}' ({{fraction}})?",
"decorations.item.label.sort": "- Sort {{value}}",
"decorations.submit.headline.edit": "Edit decoration",
"decorations.submit.headline.new": "Add new decoration",
"decorations.submit.field.name": "Name",
"decorations.submit.field.fraction": "Fraction",
"decorations.submit.field.fraction.global": "Global",
"decorations.submit.field.type": "Type",
"decorations.submit.field.type.ribbon": "Ribbon",
"decorations.submit.field.type.medal": "Medal",
"decorations.submit.field.sort": "Sort",
"decorations.submit.field.description": "Description",
"decorations.submit.field.image": "Image",
"decorations.submit.field.image.error.type": "Image is required to be in PNG format",
"decorations.submit,button.submit": "Submit",
"decorations.submit,button.cancel": "Cancel",
"ranks.list.button.add": "Add new rank",
"ranks.list.button.delete": "Delete",
"ranks.list.delete.confirm": "Do you really want to delete the rank '{{name}}' ({{fraction}})?",
"ranks.list.item.label.level": "- Level {{level}}",
"ranks.submit.headline.new": "Add new rank",
"ranks.submit.headline.edit": "Edit rank",
"ranks.submit.field.name": "Name",
"ranks.submit.field.fraction": "Fraction",
"ranks.submit.field.level": "Level",
"ranks.submit.field.image": "Image",
"ranks.submit.field.image.error.format": "Image is required to be in PNG format",
"ranks.submit.button.submit": "Submit",
"ranks.submit.button.cancel": "Cancel",
"squad.list.tooltip.delete": "Delete",
"squad.list.delete.confirm": "Do you really want to delete the squad '{{name}}' ({{fraction}})?",
"squad.list.tooltip.new": "Add new squad",
"squad.submit.new.headline": "Add new squad",
"squad.submit.edit.headline": "Edit squad",
"squad.submit.field.name": "Name",
"squad.submit.field.fraction": "Fraction",
"squad.submit.field.sort": "Sort",
"squad.submit.field.logo": "Logo",
"squad.submit.error.logo.type": "Image is required to be in PNG format",
"squad.submit.button.submit": "Submit",
"squad.submit.button.cancel": "Cancel",
"users.list.tooltip.new": "Add new user",
"users.list.tooltip.delete": "Delete",
"users.list.tooltip.awards": "Awards",
"users.list.filter.no.squad": "No squad",
"users.list.item.label.no.squad": "no squad/fraktion",
"users.list.delete.confirm": "Do you really want to delete the user '{{name}}'?",
"users.award.headline": "Decorate user",
"users.award.field.decoration": "Decoration",
"users.award.field.decoration.placeholder": "Choose...",
"users.award.field.reason": "Reason",
"users.award.field.reason.placeholder": "Enter reason...",
"users.award.button.submit": "Submit",
"users.award.button.cancel": "Cancel",
"users.award.table.head.image": "Image",
"users.award.table.head.name": "Title",
"users.award.table.head.reason": "Reason",
"users.award.table.head.date": "Date",
"users.award.table.head.status": "Status",
"users.award.table.button.delete": "Delete",
"users.award.table.status.in.progress": "In Progress",
"users.award.table.status.approved": "Approved",
"users.award.table.status.rejected": "Rejected",
"user.submit.headline.new": "Add new user",
"user.submit.headline.edit": "Edit user",
"user.submit.field.name": "Name",
"user.submit.field.squad": "Squad",
"user.submit.field.squad.not.assigned": "No fraktion/ squad",
"user.submit.field.rank": "Rank",
"user.submit.button.submit": "Submit",
"user.submit.button.cancel": "Cancel"
}

View File

@ -1,2 +1,72 @@
{
"public.error.message.required": "{{fieldName}} is a required field",
"public.error.message.min.length": "{{fieldName}} requires at least {{boundary}} characters",
"public.error.message.max.length": "{{fieldName}} is limited to {{boundary}} characters",
"public.error.message.email": "Please enter a valid eMail address",
"public.error.message.no.user": "The choosen user does not exist",
"public.error.message.default": "{{fieldName}} is not valid",
"public.common.search.button": "Search",
"request.confirm.award.headline": "Open Requests - Awards",
"request.confirm.award.table.head.participant": "Participant",
"request.confirm.award.table.head.award": "Decoration",
"request.confirm.award.table.head.reason": "Reason",
"request.confirm.award.table.head.requester": "Applicant",
"request.confirm.award.table.head.date": "Date",
"request.confirm.award.table.head.action": "Action",
"request.confirm.award.table.reject.reason.placeholder": "Reason for reject (optional)",
"request.confirm.award.table.button.action.accept": "Approve",
"request.confirm.award.table.button.action.reject": "Reject",
"request.confirm.promotion.headline": "Open Requests - Promotions",
"request.confirm.promotion.table.head.participant": "Participant",
"request.confirm.promotion.table.head.rank.before": "Old Rank",
"request.confirm.promotion.table.head.rank.after": "New Rank",
"request.confirm.promotion.table.head.requester": "Applicant",
"request.confirm.promotion.table.head.date": "Date",
"request.confirm.promotion.table.head.status": "Status",
"request.confirm.promotion.table.head.action": "Action",
"request.confirm.promotion.table.reject.reason.placeholder": "Reason for reject (optional)",
"request.confirm.promotion.table.button.action.accept": "Approve",
"request.confirm.promotion.table.button.action.reject": "Reject",
"request.confirm.promotion.table.status.progressing": "In Progress",
"request.confirm.promotion.table.status.accepted": "Approved",
"request.confirm.promotion.table.status.rejected": "Rejected",
"request.award.headline": "Request Award",
"request.award.field.user": "Participant",
"request.award.field.user.placeholder": "Choose...",
"request.award.field.award": "Decoration",
"request.award.field.award.placeholder": "Choose...",
"request.award.field.reason": "Reason",
"request.award.field.reason.placeholder": "Enter reason...",
"request.award.button.cancel": "Cancel",
"request.award.button.submit": "Submit",
"request.award.table.head.image": "Image",
"request.award.table.head.name": "Title",
"request.award.table.head.reason": "Reason",
"request.award.table.head.requester": "Applicant",
"request.award.table.head.date": "Date",
"request.award.table.head.status": "Status",
"request.award.table.head.reject.reason": "Reason for Reject",
"request.award.table.status.progressing": "In Progress",
"request.award.table.status.accepted": "Approved",
"request.award.table.status.rejected": "Rejected",
"request.promotion.headline": "Request Promotion",
"request.promotion.field.participant": "Participant",
"request.promotion.field.participant.placeholder": "Choose...",
"request.promotion.field.rank.before": "Current Rank",
"request.promotion.field.rank.after": "New Rank",
"request.promotion.button.submit": "Submit",
"request.promotion.button.cancel": "Cancel",
"request.promotion.table.head.participant": "Participant",
"request.promotion.table.head.rank.before": "Old Rank",
"request.promotion.table.head.rank.after": "New Rank",
"request.promotion.table.head.requester": "Applicant",
"request.promotion.table.head.date": "Date",
"request.promotion.table.head.status": "Status",
"request.promotion.table.head.reject.reason": "Reason for Reject",
"request.sql.dashboard.headline":"SQL Dashboard"
}

View File

@ -1,3 +1,86 @@
{
"public.error.message.required": "{{fieldName}} is a required field",
"public.error.message.min.length": "{{fieldName}} requires at least {{boundary}} characters",
"public.error.message.max.length": "{{fieldName}} is limited to {{boundary}} characters",
"public.error.message.email": "Please enter a valid eMail address",
"public.error.message.no.user": "The choosen user does not exist",
"public.error.message.default": "{{fieldName}} is not valid",
"public.common.search.button": "Search",
"stats.campaign.manage.edit": "Edit",
"stats.campaign.manage.delete": "Delete",
"stats.campaign.manage.delete.confirm": "Do you really want to delete the campaign \"{{title}}\"?",
"stats.campaign.title.all.time.overview": "All time overview",
"stats.sidebar.campaign.add": "Add campaign",
"stats.sidebar.battle.add": "Add battle",
"stats.sidebar.overview": "Overview",
"stats.sidebar.highscore": "Highscore",
"stats.sidebar.battles": "Battles",
"stats.sidebar.battles.battle.from.date": "at",
"stats.sidebar.battle.manage.edit": "Edit",
"stats.sidebar.battle.manage.delete": "Delete",
"stats.sidebar.battle.manage.delete.confirm": "Do you really want to delete the battle \"{{title}}\"?",
"stats.graph.select.sum.points": "Total Score",
"stats.graph.select.battle.points": "Score per Battle",
"stats.graph.select.player.count": "Player Count",
"stats.fraction.select.points": "Score",
"stats.fraction.select.budget": "Budget",
"stats.fraction.select.kills": "Kills",
"stats.fraction.select.friendly.fire": "Friendly Fire",
"stats.fraction.select.vehicle.kills": "Vehicle Kills",
"stats.fraction.select.air.transport": "Air Transport",
"stats.fraction.select.revive": "Revive",
"stats.fraction.select.stabilize": "Stabilized",
"stats.fraction.select.flag": "Flag Possession",
"stats.player.detail.headline": "Campaign Details - {{name}}",
"stats.player.detail.button.back": "Back",
"stats.player.detail.kill.death.ratio": "Kill/Death",
"stats.player.detail.respawn.death.ratio": "Respawn/Death",
"stats.player.detail.graph.average": "Average",
"stats.scoreboard.standings": "Final Score:",
"stats.scoreboard.participants": "Participants:",
"stats.scoreboard.show.logs": "Show Logfile",
"stats.scoreboard.download.csv": "Download CSV",
"stats.scoreboard.tab.scoreboard": "Scoreboard",
"stats.scoreboard.tab.fractions": "Fractions",
"stats.scoreboard.tab.player": "Player",
"stats.scoreboard.fraction.filter.all": "All",
"stats.scoreboard.header.player": "Player",
"stats.scoreboard.header.fraction": "Fraction",
"stats.scoreboard.header.kill": "Kills",
"stats.scoreboard.header.friendly.fire": "Friendly Fire",
"stats.scoreboard.header.revive": "Revive",
"stats.scoreboard.header.capture": "Flag Capture",
"stats.scoreboard.header.vehicle.light": "Vehicle (Light)",
"stats.scoreboard.header.vehicle.heavy": "Vehicle (Heavy)",
"stats.scoreboard.header.vehicle.air": "Vehicle (Air)",
"stats.scoreboard.header.death": "Death",
"stats.scoreboard.header.respawn": "Respawn",
"stats.scoreboard.button.detail": "Campaign statistics for {{name}}",
"stats.highscore.filter.placholder": "Player Name (separate multiple using '&')",
"stats.highscore.filter.button": "Filter",
"stats.highscore.header.name": "Name",
"stats.war.submit.headline.new": "Add new battöe",
"stats.war.submit.headline.edit": "Edit battle",
"stats.war.submit.title": "Title",
"stats.war.submit.campaign": "Campaign",
"stats.war.submit.logfile": "Logfile",
"stats.war.submit.points": "Score",
"stats.war.submit.final.budget": "Final budget",
"stats.war.submit.button.submit": "Submit",
"stats.war.submit.button.cancel": "Cancel",
"stats.war.submit.error.file.format": "Logfile requires RPT, LOG or TXT format",
"stats.campaign.submit.headline.new": "Add new campaign",
"stats.campaign.submit.headline.edit": "Edit campaign",
"stats.campaign.submit.title": "Title",
"stats.campaign.submit.button.submit": "Submit",
"stats.campaign.submit.button.cancel": "Cancel"
}