Merge branch 'use-translation-for-configured-language' into 'master'

Make use of the configured language in OpenLP in order to use translations in Web Remote

See merge request openlp/web-remote!86
This commit is contained in:
Chris Witterholt 2024-04-11 18:04:54 +00:00
commit bf0e14caee
30 changed files with 1911 additions and 1380 deletions

View File

@ -24,14 +24,42 @@ To run the web remote, run the following command:
.. code::
yarn run
yarn start
To build the web remote manually for deployment:
.. code::
yarn build --prod --aot
yarn build --aot
To lint the web remote:
.. code::
yarn lint
To audit the web remote:
.. code::
yarn audit
To run unit tests on the web remote using the Chrome browser:
.. code::
yarn test --browsers Chrome
To run unit tests on the web remote using the Microsoft Edge browser:
.. code::
yarn test --browsers Edge
Deployment

View File

@ -24,17 +24,19 @@
"supportedBrowsers": "(echo module.exports = && browserslist-useragent-regexp --allowHigherVersions) > src/assets/supportedBrowsers.js"
},
"dependencies": {
"@angular/animations": "^17.3.3",
"@angular/cdk": "^17.3.3",
"@angular/common": "^17.3.3",
"@angular/compiler": "^17.3.3",
"@angular/core": "^17.3.3",
"@angular/forms": "^17.3.3",
"@angular/material": "^17.3.3",
"@angular/platform-browser": "^17.3.3",
"@angular/platform-browser-dynamic": "^17.3.3",
"@angular/router": "^17.3.3",
"@angular/animations": "^17.3.4",
"@angular/cdk": "^17.3.4",
"@angular/common": "^17.3.4",
"@angular/compiler": "^17.3.4",
"@angular/core": "^17.3.4",
"@angular/forms": "^17.3.4",
"@angular/material": "^17.3.4",
"@angular/platform-browser": "^17.3.4",
"@angular/platform-browser-dynamic": "^17.3.4",
"@angular/router": "^17.3.4",
"@fontsource/roboto": "^5.0.12",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"core-js": "^3.36.1",
"hammerjs": "^2.0.8",
"material-icons": "^1.13.12",
@ -42,23 +44,23 @@
"zone.js": "^0.14.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.3",
"@angular-devkit/build-angular": "^17.3.4",
"@angular-eslint/builder": "^17.3.0",
"@angular-eslint/eslint-plugin": "^17.3.0",
"@angular-eslint/eslint-plugin-template": "^17.3.0",
"@angular-eslint/schematics": "^17.3.0",
"@angular-eslint/template-parser": "^17.3.0",
"@angular/cli": "~17.3.3",
"@angular/compiler-cli": "^17.3.3",
"@angular/language-service": "^17.3.3",
"@angular/cli": "~17.3.4",
"@angular/compiler-cli": "^17.3.4",
"@angular/language-service": "^17.3.4",
"@chiragrupani/karma-chromium-edge-launcher": "^2.3.1",
"@types/jasmine": "~5.1.4",
"@types/jasminewd2": "~2.0.13",
"@types/node": "~20.12.5",
"@typescript-eslint/eslint-plugin": "7.5.0",
"@typescript-eslint/parser": "7.5.0",
"@types/node": "~20.12.7",
"@typescript-eslint/eslint-plugin": "7.6.0",
"@typescript-eslint/parser": "7.6.0",
"browserslist": "^4.23.0",
"browserslist-useragent-regexp": "^4.1.2",
"browserslist-useragent-regexp": "^4.1.3",
"eslint": "^8.57.0",
"eslint-plugin-import": "~2.29.1",
"eslint-plugin-jsdoc": "~48.2.3",
@ -71,7 +73,7 @@
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "^2.1.0",
"ts-node": "~10.9.2",
"typescript": "~5.4.4"
"typescript": "~5.4.5"
},
"private": true
}

View File

@ -1,16 +1,21 @@
<mat-toolbar color="primary">
<mat-toolbar-row>
<button mat-icon-button (click)="menu.toggle()"><mat-icon>menu</mat-icon></button>
<span class="page-title">{{pageTitle}}</span>
<span class="page-title">{{ pageTitle | translate }}</span>
<span class="spacer"></span>
<button *ngIf="showLogin" mat-button (click)="login()">Login</button>
<button
<button *ngIf="showLogin" mat-button (click)="login()">{{ 'LOGIN' | translate }}</button>
<button *ngIf="webSocketOpen"
mat-icon-button
(click)="forceWebSocketReconnection()"
class="connection-status"
[matTooltip]="webSocketOpen ? 'Connected to OpenLP' : 'Disconnected'"
>
matTooltip="{{ 'CONNECTED_TO_OPENLP' | translate }}">
<mat-icon *ngIf="webSocketOpen">link</mat-icon>
</button>
<button *ngIf="!webSocketOpen"
mat-icon-button
(click)="forceWebSocketReconnection()"
class="connection-status"
matTooltip="{{ 'DISCONNECTED' | translate }}">
<mat-icon *ngIf="!webSocketOpen">link_off</mat-icon>
</button>
<span class="app-version">v{{appVersion}}</span>
@ -19,28 +24,63 @@
<mat-sidenav-container>
<mat-sidenav #menu mode="over">
<mat-nav-list>
<a mat-list-item (click)="menu.close()" routerLink="/service" routerLinkActive #serviceRoute="routerLinkActive" [activated]="serviceRoute.isActive">
<mat-icon>list</mat-icon> Service
<a mat-list-item
(click)="menu.close()"
routerLink="/service"
routerLinkActive #serviceRoute="routerLinkActive"
[activated]="serviceRoute.isActive">
<mat-icon>list</mat-icon> {{ 'SERVICE' | translate }}
</a>
<a mat-list-item (click)="menu.close()" routerLink="/slides" routerLinkActive #slidesRoute="routerLinkActive" [activated]="slidesRoute.isActive">
<mat-icon>collections</mat-icon> Slides
<a mat-list-item
(click)="menu.close()"
routerLink="/slides"
routerLinkActive #slidesRoute="routerLinkActive"
[activated]="slidesRoute.isActive">
<mat-icon>collections</mat-icon> {{ 'SLIDES' | translate }}
</a>
<a mat-list-item (click)="menu.close()" routerLink="/alerts" routerLinkActive #alertsRoute="routerLinkActive" [activated]="alertsRoute.isActive">
<mat-icon>error</mat-icon> Alerts
<a mat-list-item
(click)="menu.close()"
routerLink="/alerts"
routerLinkActive #alertsRoute="routerLinkActive"
[activated]="alertsRoute.isActive">
<mat-icon>error</mat-icon> {{ 'ALERTS' | translate }}
</a>
<a mat-list-item (click)="menu.close()" routerLink="/search" routerLinkActive #searchRoute="routerLinkActive" [activated]="searchRoute.isActive">
<mat-icon>search</mat-icon> Search
<a mat-list-item
(click)="menu.close()"
routerLink="/search"
routerLinkActive #searchRoute="routerLinkActive"
[activated]="searchRoute.isActive">
<mat-icon>search</mat-icon> {{ 'SEARCH' | translate }}
</a>
<a mat-list-item (click)="menu.close()" routerLink="/themes" routerLinkActive #themesRoute="routerLinkActive" [activated]="themesRoute.isActive">
<mat-icon>image</mat-icon> Themes
<a mat-list-item
(click)="menu.close()"
routerLink="/themes"
routerLinkActive #themesRoute="routerLinkActive"
[activated]="themesRoute.isActive">
<mat-icon>image</mat-icon> {{ 'THEMES' | translate }}
</a>
<mat-divider></mat-divider>
<a mat-list-item (click)="menu.close()" routerLink="/main">Main View</a>
<a mat-list-item (click)="menu.close()" routerLink="/stage">Stage View</a>
<a mat-list-item (click)="menu.close()" routerLink="/chords">Chord View</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/main">
{{ 'MAIN_VIEW' | translate }}
</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/stage">
{{ 'STAGE_VIEW' | translate }}
</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/chords">
{{ 'CHORD_VIEW' | translate }}
</a>
<mat-divider></mat-divider>
<a mat-list-item (click)="menu.close()" routerLink="/settings" routerLinkActive #settingsRoute="routerLinkActive" [activated]="settingsRoute.isActive">
<mat-icon>settings</mat-icon> Settings
<a mat-list-item (click)="menu.close()"
routerLink="/settings"
routerLinkActive #settingsRoute="routerLinkActive"
[activated]="settingsRoute.isActive">
<mat-icon>settings</mat-icon> {{ 'SETTINGS' | translate }}
</a>
</mat-nav-list>
</mat-sidenav>
@ -55,68 +95,164 @@
<mat-toolbar *ngIf="fastSwitching" class="toolbar-padding"></mat-toolbar>
<footer>
<mat-toolbar class="footer">
<button *ngIf="bigDisplayButtons" mat-fab color="primary" (click)="previousItem()" matTooltip="Previous item" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
(click)="previousItem()"
matTooltip="{{ 'PREVIOUS_ITEM' | translate }}"
matTooltipPosition="above">
<mat-icon>first_page</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button (click)="previousItem()" matTooltip="Previous item" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button (click)="previousItem()"
matTooltip="{{ 'PREVIOUS_ITEM' | translate }}"
matTooltipPosition="above">
<mat-icon>first_page</mat-icon>
</button>
<button *ngIf="bigDisplayButtons" mat-fab color="primary" (click)="nextItem()" matTooltip="Next item" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
(click)="nextItem()"
matTooltip="{{ 'NEXT_ITEM' | translate }}"
matTooltipPosition="above">
<mat-icon>last_page</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button (click)="nextItem()" matTooltip="Next item" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button (click)="nextItem()"
matTooltip="{{ 'NEXT_ITEM' | translate }}"
matTooltipPosition="above">
<mat-icon>last_page</mat-icon>
</button>
<button *ngIf="bigDisplayButtons" mat-fab color="primary" (click)="previousSlide()" matTooltip="Previous slide" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
(click)="previousSlide()"
matTooltip="{{ 'PREVIOUS_SLIDE' | translate }}"
matTooltipPosition="above">
<mat-icon>navigate_before</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button (click)="previousSlide()" matTooltip="Previous slide" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button (click)="previousSlide()"
matTooltip="{{ 'PREVIOUS_SLIDE' | translate }}"
matTooltipPosition="above">
<mat-icon>navigate_before</mat-icon>
</button>
<button *ngIf="bigDisplayButtons" mat-fab color="primary" (click)="nextSlide()" matTooltip="Next slide" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
(click)="nextSlide()"
matTooltip="{{ 'NEXT_SLIDE' | translate }}"
matTooltipPosition="above">
<mat-icon>navigate_next</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button (click)="nextSlide()" matTooltip="Next slide" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button (click)="nextSlide()"
matTooltip="{{ 'NEXT_SLIDE' | translate }}"
matTooltipPosition="above">
<mat-icon>navigate_next</mat-icon>
</button>
<button *ngIf="bigDisplayButtons" mat-fab color="primary" #squashedDisplayButton (click)="openDisplayModeSelector()" class="squashed-display-button" matTooltip="Change Display Mode" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
#squashedDisplayButton
(click)="openDisplayModeSelector()"
class="squashed-display-button"
matTooltip="{{ 'CHANGE_DISPLAY_MODE' | translate }}"
matTooltipPosition="above">
<mat-icon *ngIf="state.blank">videocam_off</mat-icon>
<mat-icon *ngIf="state.theme">wallpaper</mat-icon>
<mat-icon *ngIf="state.display">desktop_windows</mat-icon>
<mat-icon *ngIf="state.live()">videocam</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button #squashedDisplayButton (click)="openDisplayModeSelector()" class="squashed-display-button" matTooltip="Change Display Mode" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button
#squashedDisplayButton
(click)="openDisplayModeSelector()"
class="squashed-display-button"
matTooltip="{{ 'CHANGE_DISPLAY_MODE' | translate }}"
matTooltipPosition="above">
<mat-icon *ngIf="state.blank">videocam_off</mat-icon>
<mat-icon *ngIf="state.theme">wallpaper</mat-icon>
<mat-icon *ngIf="state.display">desktop_windows</mat-icon>
<mat-icon *ngIf="state.live()">videocam</mat-icon>
</button>
<button *ngIf="bigDisplayButtons" mat-fab color="primary" (click)="blankDisplay()" class="displayButton" [class.active]="state.blank" [disabled]="state.blank" matTooltip="Show black" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
(click)="blankDisplay()"
class="displayButton"
[class.active]="state.blank"
[disabled]="state.blank"
matTooltip="{{ 'SHOW_BLACK' | translate }}"
matTooltipPosition="above">
<mat-icon>videocam_off</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button (click)="blankDisplay()" class="displayButton" [class.active]="state.blank" [disabled]="state.blank" matTooltip="Show black" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button (click)="blankDisplay()"
class="displayButton"
[class.active]="state.blank"
[disabled]="state.blank"
matTooltip="{{ 'SHOW_BLACK' | translate }}"
matTooltipPosition="above">
<mat-icon>videocam_off</mat-icon>
</button>
<button *ngIf="bigDisplayButtons" mat-fab color="primary" (click)="themeDisplay()" class="displayButton" [class.active]="state.theme" [disabled]="state.theme" matTooltip="Show background" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
(click)="themeDisplay()"
class="displayButton"
[class.active]="state.theme"
[disabled]="state.theme"
matTooltip="{{ 'SHOW_BACKGROUND' | translate }}"
matTooltipPosition="above">
<mat-icon>wallpaper</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button (click)="themeDisplay()" class="displayButton" [class.active]="state.theme" [disabled]="state.theme" matTooltip="Show background" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button
(click)="themeDisplay()"
class="displayButton"
[class.active]="state.theme"
[disabled]="state.theme"
matTooltip="{{ 'SHOW_BACKGROUND' | translate }}"
matTooltipPosition="above">
<mat-icon>wallpaper</mat-icon>
</button>
<button *ngIf="bigDisplayButtons" mat-fab color="primary" (click)="desktopDisplay()" class="displayButton" [class.active]="state.display" [disabled]="state.display" matTooltip="Show Desktop" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
(click)="desktopDisplay()"
class="displayButton"
[class.active]="state.display"
[disabled]="state.display"
matTooltip="{{ 'SHOW_DESKTOP' | translate }}"
matTooltipPosition="above">
<mat-icon>desktop_windows</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button (click)="desktopDisplay()" class="displayButton" [class.active]="state.display" [disabled]="state.display" matTooltip="Show Desktop" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button (click)="desktopDisplay()"
class="displayButton"
[class.active]="state.display"
[disabled]="state.display"
matTooltip="{{ 'SHOW_DESKTOP' | translate }}"
matTooltipPosition="above">
<mat-icon>desktop_windows</mat-icon>
</button>
<button *ngIf="bigDisplayButtons" mat-fab color="primary" (click)="showDisplay()" class="displayButton" [class.active]="state.display" [disabled]="state.live()" matTooltip="Show Presentation" matTooltipPosition="above">
<button *ngIf="bigDisplayButtons"
mat-fab color="primary"
(click)="showDisplay()"
class="displayButton"
[class.active]="state.display"
[disabled]="state.live()"
matTooltip="{{ 'SHOW_PRESENTATION' | translate }}"
matTooltipPosition="above">
<mat-icon>videocam</mat-icon>
</button>
<button *ngIf="!bigDisplayButtons" mat-icon-button (click)="showDisplay()" class="displayButton" [class.active]="state.display" [disabled]="state.live()" matTooltip="Show Presentation" matTooltipPosition="above">
<button *ngIf="!bigDisplayButtons"
mat-icon-button (click)="showDisplay()"
class="displayButton"
[class.active]="state.display"
[disabled]="state.live()"
matTooltip="{{ 'SHOW_PRESENTATION' | translate }}"
matTooltipPosition="above">
<mat-icon>videocam</mat-icon>
</button>
</mat-toolbar>
<nav mat-tab-nav-bar mat-stretch-tabs class="fast-switcher" [tabPanel]="tabPanel" *ngIf="fastSwitching">
<nav *ngIf="fastSwitching"
mat-tab-nav-bar mat-stretch-tabs
class="fast-switcher"
[tabPanel]="tabPanel">
<a mat-tab-link
routerLink="/service"
routerLinkActive #serviceRoute="routerLinkActive"

View File

@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { TranslateService } from '@ngx-translate/core';
import { State, Display, DisplayMode } from './responses';
import { OpenLPService, WebSocketStatus } from './openlp.service';
import { WindowRef } from './window-ref.service';
@ -32,15 +34,20 @@ export class AppComponent implements OnInit {
fastSwitching = false;
bigDisplayButtons = false;
useShortcutsFromOpenlp = false;
useLanguageFromOpenlp = false;
constructor(private pageTitleService: PageTitleService, private openlpService: OpenLPService,
private dialog: MatDialog, private bottomSheet: MatBottomSheet, private windowRef: WindowRef,
private shortcutsService: ShortcutsService, private settingsService: SettingsService) {
this.pageTitleService.pageTitleChanged$.subscribe(pageTitle => this.pageTitle = pageTitle);
constructor(private translateService: TranslateService, private pageTitleService: PageTitleService,
private openlpService: OpenLPService, private dialog: MatDialog, private bottomSheet: MatBottomSheet,
private windowRef: WindowRef, private shortcutsService: ShortcutsService, private settingsService: SettingsService) {
this.pageTitleService.pageTitleChanged$.subscribe(pageTitle => this.pageTitle = pageTitle.toUpperCase());
this.openlpService.stateChanged$.subscribe(item => this.state = item);
this.openlpService.webSocketStateChanged$.subscribe(status => this.webSocketOpen = status === WebSocketStatus.Open);
this.shortcutsService.shortcutsChanged$.subscribe(shortcuts => this.addShortcuts(shortcuts));
this.appVersion = this.windowRef.nativeWindow.appVersion || '0.0';
// This language will be used as a fallback when a translation isn't found in the current language
this.translateService.setDefaultLang('en');
this.webSocketOpen = openlpService.webSocketStatus === WebSocketStatus.Open;
// Try to force websocket reconnection as user is now focused on window and will try to interact soon
// Adding a debounce to avoid event flooding
@ -55,6 +62,12 @@ export class AppComponent implements OnInit {
}
this.openlpService.retrieveSystemInformation().subscribe(res => {
this.showLogin = res.login_required
this.useLanguageFromOpenlp = this.openlpService.assertApiVersionMinimum(2, 5)
if (this.useLanguageFromOpenlp) {
this.openlpService.getLanguage().subscribe(res => {
this.translateService.use(res.language);
});
}
this.useShortcutsFromOpenlp = this.openlpService.assertApiVersionMinimum(2, 5)
this.shortcutsService.getShortcuts(this.useShortcutsFromOpenlp);
}

View File

@ -1,7 +1,7 @@
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BrowserModule, Title } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
@ -23,6 +23,9 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
import { MatSliderModule } from '@angular/material/slider';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { AppComponent } from './app.component';
import { PageTitleService } from './page-title.service';
import { OpenLPService } from './openlp.service';
@ -50,6 +53,10 @@ import { DisplayModeSelectorComponent } from './components/display-mode-selector
import { SettingsComponent } from './components/settings/settings.component';
import { StageChordPreviewComponent } from './components/settings/stage-chord-preview/stage-chord-preview.component';
// AoT requires an exported function for factories
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
declarations: [
@ -99,7 +106,15 @@ import { StageChordPreviewComponent } from './components/settings/stage-chord-pr
MatToolbarModule,
MatTooltipModule,
MatBottomSheetModule,
MatSliderModule
MatSliderModule,
TranslateModule.forRoot({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
})
],
providers: [
PageTitleService,

View File

@ -1,7 +1,17 @@
<h3>Send an Alert</h3>
<h3>{{ 'SEND_AN_ALERT' | translate }}</h3>
<form #alertForm="ngForm">
<mat-form-field>
<input matInput [(ngModel)]="alert" type="text" name="alert" placeholder="Alert" required>
<input matInput
[(ngModel)]="alert"
type="text"
name="alert"
placeholder="{{ 'ALERT' | translate }}"
required>
</mat-form-field>
<button mat-raised-button color="primary" id="sendButton" [disabled]="!alertForm.form.valid" (click)="onSubmit(); alertForm.reset()">Send</button>
<button mat-raised-button color="primary"
id="sendButton"
[disabled]="!alertForm.form.valid"
(click)="onSubmit(); alertForm.reset()">
{{ 'SEND' | translate }}
</button>
</form>

View File

@ -27,7 +27,11 @@
</div>
</div>
<div class="toolbar">
<a class="back-button" mat-mini-fab color="" routerLink="/" [matTooltip]="'Go back to controller'" *ngIf="!embedded">
<a *ngIf="!embedded"
mat-mini-fab color=""
class="back-button"
routerLink="/"
matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate }}">
<mat-icon>arrow_back</mat-icon>
</a>
<div class="transpose">
@ -39,7 +43,9 @@
<mat-icon>keyboard_arrow_up</mat-icon>
</button>
</div>
<div class="next-service-item" [matTooltip]="'Next item'" *ngIf="!embedded && activeSlide+1 === currentSlides.length">
<div *ngIf="!embedded && activeSlide+1 === currentSlides.length"
class="next-service-item"
matTooltip="{{ 'NEXT_ITEM' | translate }}">
{{ nextServiceItemTitle }}
</div>
<div class="time">{{ (openlpService.getIsTwelveHourTime()) ? (time|date:'h:mm a') : (time|date:'HH:mm') }}</div>

View File

@ -5,7 +5,7 @@
(click)="setMode(DisplayMode.Blank)"
[disabled]="displayMode === DisplayMode.Blank">
<mat-icon class="small-icon">videocam_off</mat-icon>
<span id="caption-blank" class="caption">Show Black</span>
<span id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate }}</span>
</button>
<button mat-list-item
aria-labelledby="caption-theme"
@ -13,7 +13,7 @@
(click)="setMode(DisplayMode.Theme)"
[disabled]="displayMode === DisplayMode.Theme">
<mat-icon class="small-icon">wallpaper</mat-icon>
<span id="caption-theme" class="caption">Show Background</span>
<span id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate }}</span>
</button>
<button mat-list-item
aria-labelledby="caption-desktop"
@ -21,7 +21,7 @@
(click)="setMode(DisplayMode.Desktop)"
[disabled]="displayMode === DisplayMode.Desktop">
<mat-icon class="small-icon">desktop_windows</mat-icon>
<span id="caption-desktop" class="caption">Show Desktop</span>
<span id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate }}</span>
</button>
<button mat-list-item
aria-labelledby="caption-presentation"
@ -29,7 +29,7 @@
(click)="setMode(DisplayMode.Presentation)"
[disabled]="displayMode === DisplayMode.Presentation">
<mat-icon class="small-icon">videocam</mat-icon>
<span id="caption-presentation" class="caption">Show Presentation</span>
<span id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate }}</span>
</button>
</mat-action-list>
<mat-grid-list *ngIf="display.bigDisplayButtons" cols="2" rowHeight="2:1">
@ -43,7 +43,7 @@
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-blank" class="caption">Show Black</div>
<div id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button mat-fab color="primary"
@ -55,7 +55,7 @@
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-theme" class="caption">Show Background</div>
<div id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button mat-fab color="primary"
@ -67,7 +67,7 @@
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-desktop" class="caption">Show Desktop</div>
<div id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button mat-fab color="primary"
@ -79,6 +79,6 @@
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-presentation" class="caption">Show Presentation</div>
<div id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate }}</div>
</mat-grid-tile>
</mat-grid-list>

View File

@ -1,15 +1,29 @@
<h1 mat-dialog-title>Login</h1>
<h1 mat-dialog-title>{{ 'LOGIN' | translate }}</h1>
<form #loginForm="ngForm">
<div mat-dialog-content>
<mat-form-field>
<input matInput placeholder="Username" [(ngModel)]="username" name="username" required>
<input matInput
placeholder="{{ 'USER_NAME' | translate }}"
[(ngModel)]="username"
name="username"
required>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="password" type="password" [(ngModel)]="password" name="password" required>
<input matInput
placeholder="{{ 'PASSWORD' | translate }}"
type="password"
[(ngModel)]="password"
name="password"
required>
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-raised-button id="loginButton" color="primary" [disabled]="!loginForm.form.valid" (click)="performLogin()">Login</button>
<button mat-raised-button id="loginButton"
color="primary"
[disabled]="!loginForm.form.valid"
(click)="performLogin()">
{{ 'LOGIN' | translate }}
</button>
</div>
</form>

View File

@ -12,21 +12,36 @@
<openlp-search-options></openlp-search-options>
</div>
<mat-form-field>
<input matInput [(ngModel)]="searchText" name="searchText" placeholder="Search Text" required>
<input matInput [(ngModel)]="searchText" name="searchText" placeholder="{{ 'SEARCH_TEXT' | translate }}" required>
</mat-form-field>
<br>
<button mat-raised-button id="searchButton" color="primary" [disabled]="!searchForm.form.valid" (click)="onSubmit()">Search</button>
<button mat-raised-button id="searchButton"
color="primary"
[disabled]="!searchForm.form.valid"
(click)="onSubmit()">
{{ 'SEARCH' | translate }}
</button>
</form>
<div *ngIf="searchResults">
<h3>Search Results:</h3>
<h3>{{ 'SEARCH_RESULTS' | translate }}:</h3>
<div *ngIf="!searchResults.length">
No Results matching your search were found...
{{ 'NO_SEARCH_RESULTS' | translate }}...
</div>
<table *ngIf="searchResults.length">
<tr *ngFor="let item of searchResults">
<td>{{item[1]}}</td>
<td><button mat-button color="primary" (click)="addToService(item[0])">Add to Service</button></td>
<td><button mat-button color="accent" (click)="sendLive(item[0])">Send Live</button></td>
<td>
<button mat-button color="primary"
(click)="addToService(item[0])">
{{ 'ADD_TO_SERVICE' | translate }}
</button>
</td>
<td>
<button mat-button color="accent"
(click)="sendLive(item[0])">
{{ 'SEND_LIVE' | translate }}
</button>
</td>
</tr>
</table>
</div>

View File

@ -4,19 +4,18 @@
[item]="item"
[selected]="item.selected"
(selectItem)="onItemSelected($event)"
[tabindex]="item.id"
></openlp-service-item>
[tabindex]="item.id"></openlp-service-item>
</ng-container>
<ng-template #noServiceItems>
<div class="no-items" *ngIf="!loading">
<div class="no-items-title">
<span class="material-icons icon">info</span>
No service items.
{{ 'NO_SERVICE_ITEMS' | translate }}.
</div>
<div class="no-items-actions">
<a routerLink="/search" mat-stroked-button color="primary" size="small">
<span class="material-icons">add</span>
Add Item
{{ 'ADD_ITEM' | translate }}
</a>
</div>
</div>

View File

@ -1,36 +1,34 @@
<div class="settings-panel">
<mat-card>
<mat-card-header>
User Interface
{{ 'USER_INTERFACE' | translate }}
</mat-card-header>
<mat-card-content>
<div class="settings-item">
<mat-slide-toggle
color="primary"
[checked]="settings.fastSwitching"
(change)="setSetting('fastSwitching', $event.checked)"
>
Enable Fast Switching Panel
(change)="setSetting('fastSwitching', $event.checked)">
{{ 'ENABLE_FAST_SWITCHING_PANEL' | translate }}
</mat-slide-toggle>
</div>
<div class="settings-item">
<mat-slide-toggle
color="primary"
[checked]="settings.bigDisplayButtons"
(change)="setSetting('bigDisplayButtons', $event.checked)"
>
Enable Big Display Buttons
(change)="setSetting('bigDisplayButtons', $event.checked)">
{{ 'ENABLE_BIG_DISPLAY_BUTTONS' | translate }}
</mat-slide-toggle>
</div>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-header>
Stage and Chords Appearance
{{ 'STAGE_AND_CHORDS_APPEARANCE' | translate }}
</mat-card-header>
<mat-card-content>
<mat-tab-group>
<mat-tab label="Stage">
<mat-tab label="{{ 'STAGE' | translate }}">
<ng-template matTabContent>
<ng-container>
<openlp-stage-chord-preview stageType="stage"></openlp-stage-chord-preview>
@ -38,10 +36,10 @@
</ng-container>
</ng-template>
</mat-tab>
<mat-tab label="Chords">
<mat-tab label="{{ 'CHORDS' | translate }}">
<ng-template matTabContent>
<openlp-stage-chord-preview stageType="chords"></openlp-stage-chord-preview>
<ng-container *ngTemplateOutlet="stageSettings; context: {prefix: 'chords'}"></ng-container>
<ng-container *ngTemplateOutlet="stageSettings; context: {prefix: 'chords'}"></ng-container>
</ng-template>
</mat-tab>
</mat-tab-group>
@ -51,17 +49,15 @@
<ng-template #stageSettings let-prefix="prefix">
<div class="stage-settings">
<div class="settings-item">
<label>Font Scale: {{settings[prefix + 'FontScale'] ?? 100}}%</label>
<label>{{ 'FONT_SCALE' | translate }}: {{settings[prefix + 'FontScale'] ?? 100}}%</label>
<mat-slider
min="25"
max="200"
step="6.25"
>
step="6.25">
<input
matSliderThumb
[value]="settings[prefix + 'FontScale']"
(valueChange)="setSetting(prefix + 'FontScale', $event)"
>
(valueChange)="setSetting(prefix + 'FontScale', $event)">
</mat-slider>
</div>
</div>

View File

@ -12,4 +12,3 @@
</ng-template>
</mat-card-content>
</mat-card>

View File

@ -11,12 +11,12 @@
<div class="no-items" *ngIf="!loading">
<div class="no-items-title">
<span class="material-icons icon">info</span>
No slide items.
{{ 'NO_SLIDE_ITEMS' | translate }}.
</div>
<div class="no-items-actions">
<a routerLink="/search" mat-stroked-button color="primary" size="small">
<span class="material-icons">add</span>
Add Item to Service
{{ 'ADD_ITEM_TO_SERVICE' | translate }}
</a>
</div>
</div>

View File

@ -3,8 +3,7 @@
[class.mat-headline-2]="active"
[class.currentSlide]="active"
[class.mat-headline-4]="!active"
[class.first]="!active && slide.first_slide_of_tag"
>
[class.first]="!active && slide.first_slide_of_tag">
<ng-container *ngIf="!(slide?.img); else elseImage">
{{slide?.text}}
</ng-container>

View File

@ -1,8 +1,7 @@
<div
class="overlay"
[class.embedded]="embedded"
[style.--openlp-stage-font-scale]="fontScale"
>
[style.--openlp-stage-font-scale]="fontScale">
<div class="overlay-content">
<div class="tags">
<span *ngFor="let tag of tags" [class.active]="tag.active">{{ tag.text }}</span>
@ -15,20 +14,34 @@
</div>
</div>
<div class="toolbar">
<a class="back-button" mat-mini-fab color="" routerLink="/" [matTooltip]="'Go back to controller'" *ngIf="!embedded">
<a *ngIf="!embedded"
class="back-button"
mat-mini-fab color=""
routerLink="/"
matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate }}">
<mat-icon>arrow_back</mat-icon>
</a>
<button
<button *ngIf="showNotes"
mat-mini-fab
class="show-notes"
[matTooltip]="'Show/hide notes'"
[color]="showNotes ? 'primary' : ''"
[class.show-notes-disabled]="!showNotes"
(click)="showNotes = !showNotes"
>
matTooltip="{{ 'HIDE_NOTES' | translate }}"
[color]="primary"
[class.show-notes-disabled]="false"
(click)="showNotes = false">
<mat-icon>sticky_note_2</mat-icon>
</button>
<div class="next-service-item" [matTooltip]="'Next item'" *ngIf="!embedded && activeSlide+1 === currentSlides.length">
<button *ngIf="!showNotes"
mat-mini-fab
class="show-notes"
matTooltip="{{ 'SHOW_NOTES' | translate }}"
[color]=""
[class.show-notes-disabled]="true"
(click)="showNotes = true">
<mat-icon>sticky_note_2</mat-icon>
</button>
<div *ngIf="!embedded && activeSlide+1 === currentSlides.length"
class="next-service-item"
matTooltip="{{ 'NEXT_ITEM' | translate }}">
{{ nextServiceItemTitle }}
</div>
<div class="time">{{ (openlpService.getIsTwelveHourTime()) ? (time|date:'h:mm a') : (time|date:'HH:mm') }}</div>

View File

@ -1,17 +1,15 @@
<form #themeForm="ngForm">
<h4>Theme Options</h4>
<h4>{{ 'THEME_OPTIONS' | translate }}</h4>
<div>
<!-- Theme level menu -->
<mat-form-field>
<mat-label>Theme level</mat-label>
<mat-label>{{ 'THEME_LEVEL' | translate }}</mat-label>
<mat-select [(value)]="themeLevel">
<mat-option value="global">Global</mat-option>
<mat-option value="service">Service</mat-option>
<mat-option value="song">Song</mat-option>
<mat-option value="global">{{ 'GLOBAL' | translate }}</mat-option>
<mat-option value="service">{{ 'SERVICE' | translate }}</mat-option>
<mat-option value="song">{{ 'SONG' | translate }}</mat-option>
</mat-select>
</mat-form-field>
</div>
<!-- Themes display -->
<div class="theme-container content" *ngIf="isThemeLevelSupported()">
<div *ngFor="let theme of themeList;">
<mat-card class="theme-card" (click)='setTheme(theme.name)' [class.selected]="theme.selected">
@ -22,5 +20,7 @@
</mat-card>
</div>
</div>
<mat-error *ngIf="!isThemeLevelSupported()">Song level theme changing not supported. Change your theme level to Global or Service</mat-error>
<mat-error *ngIf="!isThemeLevelSupported()">
{{ 'SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED' | translate }}
</mat-error>
</form>

View File

@ -9,6 +9,7 @@ import {
Slide,
ServiceItem,
Theme,
Language,
MainView,
Shortcut,
SystemInformation,
@ -93,6 +94,10 @@ export class OpenLPService {
}));
}
getLanguage(): Observable<Language> {
return this.doGet(`${this.apiURL}/core/language`);
}
getMainImage(): Observable<MainView> {
return this.doGet<MainView>(`${this.apiURL}/core/live-image`);
}

View File

@ -67,6 +67,10 @@ export interface Theme {
name: string;
}
export interface Language {
language: string;
}
export interface MainView {
binary_image: string;
}

52
src/assets/i18n/de.json Normal file
View File

@ -0,0 +1,52 @@
{
"ADD_ITEM": "Element Hinzufügen",
"ADD_ITEM_TO_SERVICE": "Element zum Ablauf Hinzufügen",
"ADD_TO_SERVICE": "Zum Ablauf Hinzufügen",
"ALERT": "Hinweise",
"ALERTS": "Hinweisen",
"CHANGE_DISPLAY_MODE": "Anzeigemodus Ändern",
"CHORD_VIEW": "Akkordansicht",
"CHORDS": "Akkorde",
"CONNECTED_TO_OPENLP": "Mit OpenLP Verbunden",
"DISCONNECTED": "Getrennt",
"ENABLE_BIG_DISPLAY_BUTTONS": "Große Anzeige-Schaltflächen Aktivieren",
"ENABLE_FAST_SWITCHING_PANEL": "Schnelles Umschalten Aktivieren",
"FONT_SCALE": "Schriftgrad",
"GLOBAL": "Global",
"GO_BACK_TO_CONTROLLER": "Zurück zum Controller",
"HIDE_NOTES": "Notizen Ausblenden",
"LOGIN": "Anmelden",
"MAIN_VIEW": "Hauptansicht",
"NEXT_ITEM": "Nächstes Element",
"NEXT_SLIDE": "Nächste Folie",
"NO_SEARCH_RESULTS": "Keine Suchergebnisse Gefunden",
"NO_SERVICE_ITEMS": "Keine Ablauf Elemente",
"NO_SLIDE_ITEMS": "Keine Folien-Elemente",
"PASSWORD": "Passwort",
"PREVIOUS_ITEM": "Vorheriges Element",
"PREVIOUS_SLIDE": "Vorherige Folie",
"SEARCH": "Suche",
"SEARCH_RESULTS": "Suchergebnisse",
"SEARCH_TEXT": "Suchtext",
"SEND": "Senden",
"SEND_AN_ALERT": "Hinweise Senden",
"SEND_LIVE": "Live Senden",
"SERVICE": "Ablauf",
"SETTINGS": "Einstellungen",
"SHOW_BACKGROUND": "Hintergrund Anzeigen",
"SHOW_BLACK": "Schwarz Anzeigen",
"SHOW_DESKTOP": "Desktop Anzeigen",
"SHOW_NOTES": "Notizen Anzeigen",
"SHOW_PRESENTATION": "Präsentation Anzeigen",
"SLIDES": "Folien",
"SONG": "Lied",
"SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED": "Das Ändern des Themas auf Liedebene wird nicht unterstützt. Ändern Sie Ihr Themenlevel auf Global oder Ablauf",
"STAGE": "Bühne",
"STAGE_AND_CHORDS_APPEARANCE": "Bühnen- und Akkorddarstellung",
"STAGE_VIEW": "Bühnenansicht",
"THEMES": "Themen",
"THEME_LEVEL": "Themenlevel",
"THEME_OPTIONS": "Themenoptionen",
"USER_NAME": "Benutzername",
"USER_INTERFACE": "Benutzeroberfläche"
}

52
src/assets/i18n/en.json Normal file
View File

@ -0,0 +1,52 @@
{
"ADD_ITEM": "Add Item",
"ADD_ITEM_TO_SERVICE": "Add Item to Service",
"ADD_TO_SERVICE": "Add to Service",
"ALERT": "Alert",
"ALERTS": "Alerts",
"CHANGE_DISPLAY_MODE": "Change Display Mode",
"CHORD_VIEW": "Chord View",
"CHORDS": "Chords",
"CONNECTED_TO_OPENLP": "Connected to OpenLP",
"DISCONNECTED": "Disconnected",
"ENABLE_BIG_DISPLAY_BUTTONS": "Enable Big Display Buttons",
"ENABLE_FAST_SWITCHING_PANEL": "Enable Fast Switching Panel",
"FONT_SCALE": "Font Scale",
"GLOBAL": "Global",
"GO_BACK_TO_CONTROLLER": "Go Back to Controller",
"HIDE_NOTES": "Hide Notes",
"LOGIN": "Login",
"MAIN_VIEW": "Main View",
"NEXT_ITEM": "Next Item",
"NEXT_SLIDE": "Next Slide",
"NO_SEARCH_RESULTS": "No results matching your search were found",
"NO_SERVICE_ITEMS": "No Service Items",
"NO_SLIDE_ITEMS": "No Slide Items",
"PASSWORD": "Password",
"PREVIOUS_ITEM": "Previous Item",
"PREVIOUS_SLIDE": "Previous Slide",
"SEARCH": "Search",
"SEARCH_RESULTS": "Search Results",
"SEARCH_TEXT": "Search Text",
"SEND": "Send",
"SEND_AN_ALERT": "Send an Alert",
"SEND_LIVE": "Send Live",
"SERVICE": "Service",
"SETTINGS": "Settings",
"SHOW_BACKGROUND": "Show Background",
"SHOW_BLACK": "Show Black",
"SHOW_DESKTOP": "Show Desktop",
"SHOW_NOTES": "Show Notes",
"SHOW_PRESENTATION": "Show Presentation",
"SLIDES": "Slides",
"SONG": "Song",
"SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED": "Song level theme changing not supported. Change your theme level to Global or Service",
"STAGE": "Stage",
"STAGE_AND_CHORDS_APPEARANCE": "Stage and Chords Appearance",
"STAGE_VIEW": "Stage View",
"THEMES": "Themes",
"THEME_LEVEL": "Theme Level",
"THEME_OPTIONS": "Theme Options",
"USER_NAME": "User Name",
"USER_INTERFACE": "User Interface"
}

52
src/assets/i18n/fr.json Normal file
View File

@ -0,0 +1,52 @@
{
"ADD_ITEM": "Ajouter un Élément",
"ADD_ITEM_TO_SERVICE": "Ajouter un Élément au Service",
"ADD_TO_SERVICE": "Ajouter au Service",
"ALERT": "Alerte",
"ALERTS": "Alertes",
"CHANGE_DISPLAY_MODE": "Changer le Mode dAffichage",
"CHORD_VIEW": "Vue des Accords",
"CHORDS": "Accords",
"CONNECTED_TO_OPENLP": "Connecté à OpenLP",
"DISCONNECTED": "Déconnecté",
"ENABLE_BIG_DISPLAY_BUTTONS": "Activer les Gros Boutons dAfichage",
"ENABLE_FAST_SWITCHING_PANEL": "Activer le Panneau de Commutation Rapide",
"FONT_SCALE": "Échelle de Police",
"GLOBAL": "Global",
"GO_BACK_TO_CONTROLLER": "Retour au Contrôleur",
"HIDE_NOTES": "Masquer les Notes",
"LOGIN": "Connexion",
"MAIN_VIEW": "Vue Principale",
"NEXT_ITEM": "Élément Suivant",
"NEXT_SLIDE": "Diapositive Suivante",
"NO_SEARCH_RESULTS": "Aucun résultat correspondant à votre recherche na été trouvé",
"NO_SERVICE_ITEMS": "Aucun Élément de Service",
"NO_SLIDE_ITEMS": "Aucun Élément de Diapositive",
"PASSWORD": "Mot de Passe",
"PREVIOUS_ITEM": "Élément Précédent",
"PREVIOUS_SLIDE": "Diapositive Précédente",
"SEARCH": "Recherche",
"SEARCH_RESULTS": "Résultats de la Recherche",
"SEARCH_TEXT": "Texte de Recherche",
"SEND": "Envoyer",
"SEND_AN_ALERT": "Envoyer une Alerte",
"SEND_LIVE": "Envoyer en Direct",
"SERVICE": "Service",
"SETTINGS": "Paramètres",
"SHOW_BACKGROUND": "Afficher lArrière-plan",
"SHOW_BLACK": "Afficher écran Noir",
"SHOW_DESKTOP": "Afficher le Bureau",
"SHOW_NOTES": "Afficher les Notes",
"SHOW_PRESENTATION": "Afficher la Présentation",
"SLIDES": "Diapositives",
"SONG": "Chanson",
"SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED": "Le changement de thème au niveau de la chanson nest pas pris en charge. Changez votre niveau de thème en Global ou Service",
"STAGE": "Scène",
"STAGE_AND_CHORDS_APPEARANCE": "Apparence de la Scène et des Accords",
"STAGE_VIEW": "Vue de la Scène",
"THEMES": "Thèmes",
"THEME_LEVEL": "Niveau de Thème",
"THEME_OPTIONS": "Options de Thème",
"USER_NAME": "Nom dUtilisateur",
"USER_INTERFACE": "Interface Utilisateur"
}

52
src/assets/i18n/nl.json Normal file
View File

@ -0,0 +1,52 @@
{
"ADD_ITEM": "Voeg Item Toe",
"ADD_ITEM_TO_SERVICE": "Voeg Item Toe aan Liturgie",
"ADD_TO_SERVICE": "Voeg Toe aan Liturgie",
"ALERT": "Waarschuwing",
"ALERTS": "Waarschuwingen",
"CHANGE_DISPLAY_MODE": "Wijzig Weergave",
"CHORD_VIEW": "Akkoordenscherm",
"CHORDS": "Akkoorden",
"CONNECTED_TO_OPENLP": "Verbonden met OpenLP",
"DISCONNECTED": "Niet Verbonden",
"ENABLE_BIG_DISPLAY_BUTTONS": "Activeer Grote Beeldschermknoppen",
"ENABLE_FAST_SWITCHING_PANEL": "Activeer Snel Wisselpaneel",
"FONT_SCALE": "Schaal Lettertype",
"GLOBAL": "Globaal",
"GO_BACK_TO_CONTROLLER": "Ga Terug naar Controller",
"HIDE_NOTES": "Verberg Notities",
"LOGIN": "Log In",
"MAIN_VIEW": "Hoofdscherm",
"NEXT_ITEM": "Volgend Item",
"NEXT_SLIDE": "Volgende Slide",
"NO_SEARCH_RESULTS": "Jouw zoekopdracht heeft geen resultaten opgeleverd",
"NO_SERVICE_ITEMS": "Geen Liturgie Items",
"NO_SLIDE_ITEMS": "Geen Slide Items",
"PASSWORD": "Wachtwoord",
"PREVIOUS_ITEM": "Vorig Item",
"PREVIOUS_SLIDE": "Vorige Slide",
"SEARCH": "Zoek",
"SEARCH_RESULTS": "Zoekresultaten",
"SEARCH_TEXT": "Zoektekst",
"SEND": "Versturen",
"SEND_AN_ALERT": "Stuur een Waarschuwing",
"SEND_LIVE": "Ga Live",
"SERVICE": "Liturgie",
"SETTINGS": "Instellingen",
"SHOW_BACKGROUND": "Toon Achtergrond",
"SHOW_BLACK": "Toon Zwart",
"SHOW_DESKTOP": "Toon Bureaublad",
"SHOW_NOTES": "Toon Notities",
"SHOW_PRESENTATION": "Toon Presentatie",
"SLIDES": "Slides",
"SONG": "Lied",
"SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED": "Themawijziging op liedniveau wordt niet ondersteund. Wijzig uw themaniveau naar Globaal of Service",
"STAGE": "Podium",
"STAGE_AND_CHORDS_APPEARANCE": "Podium- en Akkoordenscherm",
"STAGE_VIEW": "Podiumscherm",
"THEMES": "Thema's",
"THEME_LEVEL": "Thema Niveau",
"THEME_OPTIONS": "Theme Opties",
"USER_NAME": "Gebruikersnaam",
"USER_INTERFACE": "Gebruikersinterface"
}

52
src/assets/i18n/sl.json Normal file
View File

@ -0,0 +1,52 @@
{
"ADD_ITEM": "Dodaj Element",
"ADD_ITEM_TO_SERVICE": "Dodaj Element Spored",
"ADD_TO_SERVICE": "Dodaj Spored",
"ALERT": "Opozorilo",
"ALERTS": "Opozorila",
"CHANGE_DISPLAY_MODE": "Spremeni način Prikaza",
"CHORD_VIEW": "Pogled Akordov",
"CHORDS": "Akordi",
"CONNECTED_TO_OPENLP": "Povezano z OpenLP",
"DISCONNECTED": "Prekinjeno",
"ENABLE_BIG_DISPLAY_BUTTONS": "Omogoči Velike Gumbe za Prikaz",
"ENABLE_FAST_SWITCHING_PANEL": "Omogoči Hitro Preklapljanje Plošče",
"FONT_SCALE": "Velikost Pisave",
"GLOBAL": "Globalno",
"GO_BACK_TO_CONTROLLER": "Vrni se na Krmilnikr",
"HIDE_NOTES": "Skrij Opombe",
"LOGIN": "Prijava",
"MAIN_VIEW": "Glavni Pogled",
"NEXT_ITEM": "Naslednji Element",
"NEXT_SLIDE": "Naslednji Diapozitiv",
"NO_SEARCH_RESULTS": "Ni rezultatov, ki bi se ujemali z vašim iskanjem",
"NO_SERVICE_ITEMS": "Ni Elementov Spored",
"NO_SLIDE_ITEMS": "Ni Diapozitivnih Elementov",
"PASSWORD": "Geslo",
"PREVIOUS_ITEM": "Prejšnji Element",
"PREVIOUS_SLIDE": "Prejšnji Diapozitiv",
"SEARCH": "Iskanje",
"SEARCH_RESULTS": "Rezultati Iskanja",
"SEARCH_TEXT": "Besedilo Iskanja",
"SEND": "Pošlji",
"SEND_AN_ALERT": "Pošlji Opozorilo",
"SEND_LIVE": "Pošlji v Živo",
"SERVICE": "Spored",
"SETTINGS": "Nastavitve",
"SHOW_BACKGROUND": "Prikaži Ozadje",
"SHOW_BLACK": "Prikaži Črno",
"SHOW_DESKTOP": "Prikaži Namizje",
"SHOW_NOTES": "Prikaži Opomb",
"SHOW_PRESENTATION": "Prikaži Predstavitve",
"SLIDES": "Diapozitivi",
"SONG": "Pesem",
"SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED": "Spreminjanje teme na ravni pesmi ni podprto. Spremenite raven teme na Globalno ali Spored",
"STAGE": "Odrom",
"STAGE_AND_CHORDS_APPEARANCE": "Videz Odra in Akordov",
"STAGE_VIEW": "Pogled Odra",
"THEMES": "Teme",
"THEME_LEVEL": "Raven Teme",
"THEME_OPTIONS": "Možnosti Teme",
"USER_NAME": "Uporabniško Ime",
"USER_INTERFACE": "Uporabniški Vmesnik"
}

2493
yarn.lock

File diff suppressed because it is too large Load Diff