Compare commits

...

54 Commits

Author SHA1 Message Date
Chris Witterholt 2a480e8c3e Merge branch 'release-9.21' into 'master'
Release 0.9.21.

See merge request openlp/web-remote!117
2024-05-18 17:39:42 +00:00
Chris Witterholt 38149d2279
Release 0.9.21. 2024-05-18 19:33:53 +02:00
Tim Bentley ed39a01b67 Merge branch 'add-missing-translation-english-autodetect' into 'master'
Add missing translation English autodetect

See merge request openlp/web-remote!116
2024-05-18 12:41:33 +00:00
Chris Witterholt f4f3d7b444 Add missing translation English autodetect 2024-05-18 12:41:33 +00:00
Chris Witterholt 7020ae0bde Merge branch 'release-9.20' into 'master'
Release 0.9.20.

See merge request openlp/web-remote!115
2024-05-16 12:51:19 +00:00
Chris Witterholt a58c13d82d
Release 0.9.20. 2024-05-16 07:47:34 +02:00
Chris Witterholt 55610e623f Merge branch 'update-angular' into 'master'
Update several packages including Angular.

See merge request openlp/web-remote!114
2024-05-16 05:34:00 +00:00
Chris Witterholt 06a172fcc1
Update several packages including Angular. 2024-05-16 07:27:07 +02:00
Chris Witterholt b29cad5225 Merge branch 'force-capitalization' into 'master'
Force capitalization in translations.

See merge request openlp/web-remote!113
2024-05-07 05:46:40 +00:00
Chris Witterholt 1144564c2c Force capitalization in translations. 2024-05-07 05:46:40 +00:00
Chris Witterholt bf185b0f92 Merge branch 'release-9.19' into 'master'
Release 0.9.19.

See merge request openlp/web-remote!110
2024-04-27 17:05:30 +00:00
Raoul Snyman 40c4d9e363 Merge branch 'remove-i18n-comments' into 'master'
Remove comments before pushing translation source

See merge request openlp/web-remote!112
2024-04-27 17:00:08 +00:00
Raoul Snyman 4a25c28f29 Remove comments before pushing translation source 2024-04-27 09:55:12 -07:00
Chris Witterholt 7b245d248b
Release 0.9.19. 2024-04-27 09:54:13 +02:00
Chris Witterholt da749d7ec8 Merge branch 'add-i18n-source-push' into 'master'
Automatically push up newest source strings to Transifex

See merge request openlp/web-remote!109
2024-04-27 07:40:00 +00:00
Raoul Snyman 08d097dd0a Automatically push up newest source strings to Transifex 2024-04-27 07:39:59 +00:00
Raoul Snyman fcc1f543d0 Merge branch 'dont-install-dev-deps' into 'master'
Don't install dev dependencies when building

See merge request openlp/web-remote!103
2024-04-26 15:54:19 +00:00
Raoul Snyman 4fd8fe0f54 Don't install dev dependencies when building 2024-04-26 15:54:18 +00:00
Chris Witterholt f657a24e99 Merge branch 'refactor-tests' into 'master'
Refactor tests.

See merge request openlp/web-remote!108
2024-04-25 19:35:14 +00:00
Chris Witterholt 037d0ccb40
Refactor tests. 2024-04-25 21:31:35 +02:00
Chris Witterholt ff8c01c874 Merge branch 'update-angular' into 'master'
Update Angular.

See merge request openlp/web-remote!107
2024-04-25 19:11:35 +00:00
Chris Witterholt 86d85870e9
Update Angular. 2024-04-25 21:07:48 +02:00
Chris Witterholt a6b4ea23c8 Merge branch 'refactor-login-component' into 'master'
Refactor login component.

See merge request openlp/web-remote!106
2024-04-25 18:55:52 +00:00
Chris Witterholt 75c11d4543
Refactor login component. 2024-04-25 20:52:04 +02:00
Chris Witterholt ece665dbf2 Merge branch 'fix-live-button' into 'master'
Fix the live button.

See merge request openlp/web-remote!105
2024-04-25 18:32:58 +00:00
Chris Witterholt b62f21bae0
Fix the live button. 2024-04-25 20:28:53 +02:00
Chris Witterholt 18ba69088b Merge branch 'add-missing-translations' into 'master'
Add missing translations.

See merge request openlp/web-remote!104
2024-04-25 04:06:46 +00:00
Chris Witterholt 89c908ef4a Add missing translations. 2024-04-25 04:06:46 +00:00
Chris Witterholt ee9392cc01 Merge branch 'use-angular17-template-syntax' into 'master'
Use the new Angular 17 template syntax and property 'styleUrl'

See merge request openlp/web-remote!102
2024-04-24 16:44:06 +00:00
Chris Witterholt ea39bbc3d7 Use the new Angular 17 template syntax and property 'styleUrl' 2024-04-24 16:44:06 +00:00
Raoul Snyman 544b400829 Merge branch 'update-translations' into 'master'
Update translations from Transifex

See merge request openlp/web-remote!101
2024-04-20 22:12:30 +00:00
Raoul Snyman db8202983f Update translations from Transifex 2024-04-19 23:13:27 -07:00
Raoul Snyman ce9fa57d9e Merge branch 'update-arg-parsing' into 'master'
Some minor updates

See merge request openlp/web-remote!100
2024-04-20 05:36:11 +00:00
Raoul Snyman a6f348d71b Some minor updates 2024-04-20 05:36:11 +00:00
Raoul Snyman 55cccff3ed Merge branch 'integrate-with-transifex' into 'master'
Create a script to upload/download translations from Transifex

See merge request openlp/web-remote!98
2024-04-19 23:30:08 +00:00
Raoul Snyman 3be49dbd74 Create a script to upload/download translations from Transifex 2024-04-19 11:35:14 -07:00
Chris Witterholt ed3b71ef9e Merge branch 'update-packages' into 'master'
Update several packages including Angular.

See merge request openlp/web-remote!99
2024-04-19 16:12:30 +00:00
Chris Witterholt 5a9e1b56ac Update several packages including Angular. 2024-04-19 16:12:29 +00:00
Chris Witterholt 5550648d20 Merge branch 'correct-file-name-of-english-translation' into 'master'
Correct file name of English translation.

See merge request openlp/web-remote!97
2024-04-16 16:55:53 +00:00
Chris Witterholt 0fdcbff737
Correct file name of English translation. 2024-04-16 18:51:47 +02:00
Chris Witterholt ce064facda Merge branch 'release-9.18' into 'master'
Release 0.9.18.

See merge request openlp/web-remote!96
2024-04-15 17:16:19 +00:00
Chris Witterholt fe07f78415
Release 0.9.18. 2024-04-15 19:11:31 +02:00
Chris Witterholt 4e09f0a08d Merge branch 'remove-hammerjs' into 'master'
Remove Hammerjs.

See merge request openlp/web-remote!95
2024-04-15 16:12:06 +00:00
Chris Witterholt 2e58a547ac
Remove Hammerjs. 2024-04-15 17:42:25 +02:00
Chris Witterholt 87aefcdbf0 Merge branch 'workaround-for-not-found-english-translation-file' into 'master'
Add workaround for missing default English translation file when using Web API 2.4 or older.

See merge request openlp/web-remote!94
2024-04-15 15:24:28 +00:00
Chris Witterholt b44be59d06 Add workaround for missing default English translation file when using Web API 2.4 or older. 2024-04-15 15:24:28 +00:00
Chris Witterholt a255c0f00e Merge branch 'release-9.17' into 'master'
Release 0.9.17.

See merge request openlp/web-remote!93
2024-04-14 18:10:16 +00:00
Chris Witterholt 64b4713e43
Release 0.9.17. 2024-04-14 20:05:14 +02:00
Chris Witterholt f34830dee6 Merge branch 'workaround-for-not-found-english-translation-file' into 'master'
Add workaround for missing default English translation file when using Web API 2.4 or older.

See merge request openlp/web-remote!92
2024-04-14 18:01:48 +00:00
Chris Witterholt c9ff11dd58
Add workaround for missing default English translation file when using Web API 2.4 or older. 2024-04-14 19:14:22 +02:00
Chris Witterholt f0554b8e53 Merge branch 'correct-slovenian-translation' into 'master'
Correct Slovenian translation

See merge request openlp/web-remote!91
2024-04-14 13:13:39 +00:00
Chris Witterholt 93a7786d8a Correct Slovenian translation 2024-04-14 13:13:38 +00:00
Chris Witterholt 6bc707dc46 Merge branch 'upload-to-new-server' into 'master'
Change the server we upload releases to

See merge request openlp/web-remote!90
2024-04-13 18:09:33 +00:00
Raoul Snyman fcbc845ae6 Change the server we upload releases to 2024-04-13 11:05:48 -07:00
90 changed files with 3430 additions and 2463 deletions

1
.gitignore vendored
View File

@ -25,6 +25,7 @@
!.vscode/extensions.json
# misc
/.nx
/.sass-cache
/connect.lock
/coverage

View File

@ -10,8 +10,8 @@ audit:
script:
- yarn install
- yarn audit
except:
- tags
rules:
- when: always
allow_failure: true
lint:
@ -19,44 +19,62 @@ lint:
script:
- yarn install
- yarn lint
except:
- tags
rules:
- when: always
test:
stage: test
script:
- yarn install
- yarn test --no-progress --no-watch --browsers=ChromiumHeadlessCI
except:
- tags
rules:
- when: always
push-i18n-source:
stage: build
script:
- apk add npm
- npm install @transifex/api
- npm run tx push
rules:
- changes:
- src/assets/en.json
build-branch:
stage: build
script:
- yarn install
# Temporarily install npm manually until we get it added to the runner image
- apk add npm
- yarn install --production
- npm install @angular/cli
- yarn build --no-progress --configuration production --aot
- export APP_VERSION=`git describe --dirty --tags --long --match '*[0-9]*'`
- 'echo "window.appVersion = \"$APP_VERSION\";" > dist/web-remote/assets/version.js'
artifacts:
paths:
- dist/
only:
- branches
rules:
- if: $CI_COMMIT_BRANCH != "master"
build-tag:
stage: build
script:
- yarn install
# Temporarily install npm manually until we get it added to the runner image
- apk add npm
- yarn install --production
- npm install @angular/cli
- yarn build --no-progress --configuration production --aot
- 'echo "window.appVersion = \"$CI_COMMIT_TAG\";" > dist/web-remote/assets/version.js'
artifacts:
paths:
- dist/
only:
- tags
rules:
- if: $CI_COMMIT_TAG
deploy:
stage: deploy
variables:
OPENLP_HOST: "new.openlp.io"
script:
- cd dist/web-remote
- zip -r ../../remote-$CI_COMMIT_TAG.zip *
@ -70,10 +88,10 @@ deploy:
- eval $(ssh-agent -s)
- ssh-add ~/.ssh/id_rsa
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- ssh openlp@openlp.io "mkdir -p /home/openlp/sites/get.openlp.org/www/remote/$CI_COMMIT_TAG"
- scp remote-$CI_COMMIT_TAG.zip openlp@openlp.io:/home/openlp/sites/get.openlp.org/www/remote/$CI_COMMIT_TAG/
- scp version-$CI_COMMIT_TAG.json openlp@openlp.io:/home/openlp/sites/get.openlp.org/www/remote/
- ssh openlp@openlp.io "rm /home/openlp/sites/get.openlp.org/www/remote/version.json"
- scp version-$CI_COMMIT_TAG.json openlp@openlp.io:/home/openlp/sites/get.openlp.org/www/remote/version.json
only:
- tags
- ssh openlp@$OPENLP_HOST "mkdir -p /home/openlp/sites/get.openlp.org/www/remote/$CI_COMMIT_TAG"
- scp remote-$CI_COMMIT_TAG.zip openlp@$OPENLP_HOST:/home/openlp/sites/get.openlp.org/www/remote/$CI_COMMIT_TAG/
- scp version-$CI_COMMIT_TAG.json openlp@$OPENLP_HOST:/home/openlp/sites/get.openlp.org/www/remote/
- ssh openlp@$OPENLP_HOST "rm /home/openlp/sites/get.openlp.org/www/remote/version.json"
- scp version-$CI_COMMIT_TAG.json openlp@$OPENLP_HOST:/home/openlp/sites/get.openlp.org/www/remote/version.json
rules:
- if: $CI_COMMIT_TAG

View File

@ -1,6 +1,40 @@
Changes in OpenLP Web Remote
============================
Version 0.9.21
--------------
* Force capitalization in translations when version 3.1.2 or newer is installed.
* Updated zone.js.
Version 0.9.20
--------------
* Force capitalization in translations when version 3.1.2 or newer is installed.
* Updated dependencies including Angular.
Version 0.9.19
--------------
* Hook Web Remote up to Transifex regarding the translations.
* Add some missing translations.
* Make sure that the English translation will function in Web Remote when version 3.1.2 or newer is installed.
* The page title is not capitalized anymore.
* Prevent a TypeError in the browser console when Settings is displayed and there are no live items.
* Updated dependencies including Angular.
Version 0.9.18
--------------
* Prevent HTTP 404 error in browser console when using Web API 2.4 or older.
* Remove Hammerjs.
Version 0.9.17
--------------
* Add workaround for missing default English translation file when using Web API 2.4 or older.
* Correct Slovenian translation.
Version 0.9.16
--------------

View File

@ -1,6 +1,6 @@
{
"name": "@openlp/web-remote",
"version": "0.9.16",
"version": "0.9.21",
"description": "The web remote for OpenLP, written in Angular",
"keywords": [
"OpenLP",
@ -21,49 +21,52 @@
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"supportedBrowsers": "(echo module.exports = && browserslist-useragent-regexp --allowHigherVersions) > src/assets/supportedBrowsers.js"
"supportedBrowsers": "(echo module.exports = && browserslist-useragent-regexp --allowHigherVersions) > src/assets/supportedBrowsers.js",
"tx": "node scripts/tx.js"
},
"dependencies": {
"@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",
"@angular/animations": "^17.3.9",
"@angular/cdk": "^17.3.9",
"@angular/common": "^17.3.9",
"@angular/compiler": "^17.3.9",
"@angular/core": "^17.3.9",
"@angular/forms": "^17.3.9",
"@angular/material": "^17.3.9",
"@angular/platform-browser": "^17.3.9",
"@angular/platform-browser-dynamic": "^17.3.9",
"@angular/router": "^17.3.9",
"@fontsource/roboto": "^5.0.13",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"core-js": "^3.36.1",
"hammerjs": "^2.0.8",
"core-js": "^3.37.1",
"material-icons": "^1.13.12",
"rxjs": "^7.8.1",
"zone.js": "^0.14.4"
"zone.js": "^0.14.6"
},
"devDependencies": {
"@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.4",
"@angular/compiler-cli": "^17.3.4",
"@angular/language-service": "^17.3.4",
"@angular-devkit/build-angular": "^17.3.7",
"@angular-eslint/builder": "^17.4.1",
"@angular-eslint/eslint-plugin": "^17.4.1",
"@angular-eslint/eslint-plugin-template": "^17.4.1",
"@angular-eslint/schematics": "^17.4.1",
"@angular-eslint/template-parser": "^17.4.1",
"@angular/cli": "~17.3.7",
"@angular/compiler-cli": "^17.3.9",
"@angular/language-service": "^17.3.9",
"@chiragrupani/karma-chromium-edge-launcher": "^2.3.1",
"@transifex/api": "^7.1.0",
"@types/jasmine": "~5.1.4",
"@types/jasminewd2": "~2.0.13",
"@types/node": "~20.12.7",
"@typescript-eslint/eslint-plugin": "7.6.0",
"@typescript-eslint/parser": "7.6.0",
"@types/jest": "^29.5.12",
"@types/node": "~20.12.12",
"@typescript-eslint/eslint-plugin": "7.9.0",
"@typescript-eslint/parser": "7.9.0",
"axios": "^1.6.8",
"browserslist": "^4.23.0",
"browserslist-useragent-regexp": "^4.1.3",
"eslint": "^8.57.0",
"eslint-plugin-import": "~2.29.1",
"eslint-plugin-jsdoc": "~48.2.3",
"eslint-plugin-jsdoc": "~48.2.5",
"eslint-plugin-prefer-arrow": "~1.2.3",
"jasmine-core": "~5.1.2",
"jasmine-spec-reporter": "~7.0.0",

158
scripts/tx.js Normal file
View File

@ -0,0 +1,158 @@
const fs = require('fs');
const process = require('process');
const path = require('path');
const { parseArgs } = require('util');
const { transifexApi } = require('@transifex/api');
const axios = require('axios');
const ACTIONS = ['push', 'upload', 'download'];
// Parse the command line arguments
function parseCliArgs() {
const options = {
token: {
type: 'string',
short: 't'
},
verbose: {
type: 'boolean',
short: 'v',
default: false
},
help: {
type: 'boolean',
short: 'h',
default: false
}
};
const { values, positionals } = parseArgs({options: options, allowPositionals: true});
if (values.help) {
console.log(`usage: tx [-h|--help] [-v|--verbose] [-t|--token TOKEN] <action>
positional arguments:
action the action to perform, one of 'push', 'upload' or 'download'
options:
-h, --help show this help message and exit
-e, --verbose show extra logging messages
-t TOKEN, --token TOKEN
specify a Transifex API token, can also use the TX_TOKEN environment variable
`);
process.exit(0);
}
if (!values.token && !process.env.TX_TOKEN) {
console.error("ERROR: Neither --token nor TX_TOKEN was set");
process.exit(1);
}
else if (!values.token && process.env.TX_TOKEN) {
values.token = process.env.TX_TOKEN;
}
if (positionals.length < 1 || !ACTIONS.includes(positionals[0])) {
console.error("ERROR: Action is not valid, please use one of " + ACTIONS.join(", "));
process.exit(1);
}
return {token: values.token, action: positionals[0], verbose: values.verbose};
}
function getPercentage(attributes) {
return (parseFloat(attributes.translated_strings) / parseFloat(attributes.total_strings)) * 100;
}
async function pushSource(resource, verbose) {
const filename = path.join('src', 'assets', 'en.json');
if (!fs.existsSync(filename)) {
console.error(`Source file ${filename} does not exist!`);
process.exit(1);
}
if (verbose) {
console.log('Reading en.json...');
}
let content = fs.readFileSync(filename);
let json = JSON.parse(content);
delete json._COMMENT;
content = JSON.stringify(json);
console.log('Uploading en.json...');
await transifexApi.ResourceStringsAsyncUpload.upload({
resource: resource,
content: content.toString()
});
}
async function uploadFiles(resource, languages, verbose) {
for (const lang of languages) {
const filename = path.join('src', 'assets', 'i18n', `${lang.attributes.code}.json`);
if (!fs.existsSync(filename)) {
continue;
}
if (verbose) {
console.log(`Reading ${lang.attributes.code}.json...`);
}
const content = fs.readFileSync(filename);
console.log(`Uploading ${lang.attributes.code}.json...`);
await transifexApi.ResourceTranslationsAsyncUpload.upload({
resource: resource,
language: lang,
content: content.toString()
});
}
}
async function downloadFiles(org, project, resource, languages, verbose) {
for (const lang of languages) {
if (verbose) {
console.log(`Checking completeness of ${lang.attributes.code}.json...`);
}
const trs = await transifexApi.ResourceLanguageStats.get({
project: project,
resource: resource,
language: lang
});
if (getPercentage(trs.attributes) < 50) {
continue;
}
console.log(`Downloading ${lang.attributes.code}.json...`);
const url = await transifexApi.ResourceTranslationsAsyncDownload.download({
resource: resource,
language: lang
});
const response = await axios.get(url);
if (response.status == 200) {
fs.writeFileSync(path.join('src', 'assets', 'i18n', `${lang.attributes.code}.json`), JSON.stringify(response.data, null, 2));
}
else {
console.log(`Error: ${response.statusText}`);
}
}
}
async function main() {
// Parse the command line arguments
const { token, action, verbose } = parseCliArgs();
// Set up the Transifex API
transifexApi.setup({auth: token});
if (verbose) {
console.log('Fetching organization, project and languages...');
}
const org = await transifexApi.Organization.get({slug: 'openlp'});
const projects = await org.fetch('projects');
const proj = await projects.get({slug: 'web-remote'});
const resource = await transifexApi.Resource.get({project: proj, slug: 'i18n-strings'});
const languages = await proj.fetch('languages');
await languages.fetch();
if (action == 'upload') {
await uploadFiles(resource, languages.data, verbose);
}
else if (action == 'download') {
await downloadFiles(org, proj, resource, languages.data, verbose);
}
else if (action == 'push') {
await pushSource(resource, verbose);
}
}
main();

View File

@ -1,23 +1,35 @@
<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 | translate }}</span>
<button mat-icon-button (click)="menu.toggle()">
<mat-icon>menu</mat-icon>
</button>
<span class="page-title">{{ pageTitle | translate | titlecase }}</span>
<span class="spacer"></span>
<button *ngIf="showLogin" mat-button (click)="login()">{{ 'LOGIN' | translate }}</button>
<button *ngIf="webSocketOpen"
mat-icon-button
(click)="forceWebSocketReconnection()"
class="connection-status"
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>
@if (showLogin) {
<button
mat-button
(click)="login()">
{{ 'LOGIN' | translate | titlecase }}
</button>
}
@if (webSocketOpen) {
<button
mat-icon-button
(click)="forceWebSocketReconnection()"
class="connection-status"
matTooltip="{{ 'CONNECTED_TO_OPENLP' | translate | titlecase }}">
<mat-icon>link</mat-icon>
</button>
}
@else {
<button
mat-icon-button
(click)="forceWebSocketReconnection()"
class="connection-status"
matTooltip="{{ 'DISCONNECTED' | translate | titlecase }}">
<mat-icon>link_off</mat-icon>
</button>
}
<span class="app-version">v{{appVersion}}</span>
</mat-toolbar-row>
</mat-toolbar>
@ -29,58 +41,58 @@
routerLink="/service"
routerLinkActive #serviceRoute="routerLinkActive"
[activated]="serviceRoute.isActive">
<mat-icon>list</mat-icon> {{ 'SERVICE' | translate }}
<mat-icon>list</mat-icon> {{ 'SERVICE' | translate | titlecase }}
</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/slides"
routerLinkActive #slidesRoute="routerLinkActive"
[activated]="slidesRoute.isActive">
<mat-icon>collections</mat-icon> {{ 'SLIDES' | translate }}
<mat-icon>collections</mat-icon> {{ 'SLIDES' | translate | titlecase }}
</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/alerts"
routerLinkActive #alertsRoute="routerLinkActive"
[activated]="alertsRoute.isActive">
<mat-icon>error</mat-icon> {{ 'ALERTS' | translate }}
<mat-icon>error</mat-icon> {{ 'ALERTS' | translate | titlecase }}
</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/search"
routerLinkActive #searchRoute="routerLinkActive"
[activated]="searchRoute.isActive">
<mat-icon>search</mat-icon> {{ 'SEARCH' | translate }}
<mat-icon>search</mat-icon> {{ 'SEARCH' | translate | titlecase }}
</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/themes"
routerLinkActive #themesRoute="routerLinkActive"
[activated]="themesRoute.isActive">
<mat-icon>image</mat-icon> {{ 'THEMES' | translate }}
<mat-icon>image</mat-icon> {{ 'THEMES' | translate | titlecase }}
</a>
<mat-divider></mat-divider>
<a mat-list-item
(click)="menu.close()"
routerLink="/main">
{{ 'MAIN_VIEW' | translate }}
{{ 'MAIN_VIEW' | translate | titlecase }}
</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/stage">
{{ 'STAGE_VIEW' | translate }}
{{ 'STAGE_VIEW' | translate | titlecase }}
</a>
<a mat-list-item
(click)="menu.close()"
routerLink="/chords">
{{ 'CHORD_VIEW' | translate }}
{{ 'CHORD_VIEW' | translate | titlecase }}
</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' | translate }}
<mat-icon>settings</mat-icon> {{ 'SETTINGS' | translate | titlecase }}
</a>
</mat-nav-list>
</mat-sidenav>
@ -92,188 +104,214 @@
</main>
<!-- These two toolbars are for padding the content so the real toolbars do not block content when scrolled down -->
<mat-toolbar class="toolbar-padding"></mat-toolbar>
<mat-toolbar *ngIf="fastSwitching" class="toolbar-padding"></mat-toolbar>
@if (fastSwitching) {
<mat-toolbar class="toolbar-padding"></mat-toolbar>
}
<footer>
<mat-toolbar class="footer">
<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' | translate }}"
matTooltipPosition="above">
<mat-icon>first_page</mat-icon>
</button>
<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' | translate }}"
matTooltipPosition="above">
<mat-icon>last_page</mat-icon>
</button>
<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' | translate }}"
matTooltipPosition="above">
<mat-icon>navigate_before</mat-icon>
</button>
<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' | 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' | 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' | 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' | 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' | 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' | 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' | 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' | 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' | 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' | 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' | translate }}"
matTooltipPosition="above">
<mat-icon>videocam</mat-icon>
</button>
@if (bigDisplayButtons) {
<button
mat-fab color="primary"
(click)="previousItem()"
matTooltip="{{ 'PREVIOUS_ITEM' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>first_page</mat-icon>
</button>
<button
mat-fab color="primary"
(click)="nextItem()"
matTooltip="{{ 'NEXT_ITEM' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>last_page</mat-icon>
</button>
<button
mat-fab color="primary"
(click)="previousSlide()"
matTooltip="{{ 'PREVIOUS_SLIDE' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>navigate_before</mat-icon>
</button>
<button
mat-fab color="primary"
(click)="nextSlide()"
matTooltip="{{ 'NEXT_SLIDE' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>navigate_next</mat-icon>
</button>
<button
mat-fab color="primary"
#squashedDisplayButton
(click)="openDisplayModeSelector()"
class="squashed-display-button"
matTooltip="{{ 'CHANGE_DISPLAY_MODE' | translate | titlecase }}"
matTooltipPosition="above">
@if (state.blank) {
<mat-icon>videocam_off</mat-icon>
}
@else if (state.theme) {
<mat-icon>wallpaper</mat-icon>
}
@else if (state.display) {
<mat-icon>desktop_windows</mat-icon>
}
@else if (state.live()) {
<mat-icon>videocam</mat-icon>
}
</button>
<button
mat-fab color="primary"
(click)="blankDisplay()"
class="displayButton"
[class.active]="state.blank"
[disabled]="state.blank"
matTooltip="{{ 'SHOW_BLACK' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>videocam_off</mat-icon>
</button>
<button
mat-fab color="primary"
(click)="themeDisplay()"
class="displayButton"
[class.active]="state.theme"
[disabled]="state.theme"
matTooltip="{{ 'SHOW_BACKGROUND' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>wallpaper</mat-icon>
</button>
<button
mat-fab color="primary"
(click)="desktopDisplay()"
class="displayButton"
[class.active]="state.display"
[disabled]="state.display"
matTooltip="{{ 'SHOW_DESKTOP' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>desktop_windows</mat-icon>
</button>
<button
mat-fab color="primary"
(click)="showDisplay()"
class="displayButton"
[class.active]="state.display"
[disabled]="state.live()"
matTooltip="{{ 'SHOW_PRESENTATION' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>videocam</mat-icon>
</button>
}
@else {
<button
mat-icon-button
(click)="previousItem()"
matTooltip="{{ 'PREVIOUS_ITEM' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>first_page</mat-icon>
</button>
<button
mat-icon-button
(click)="nextItem()"
matTooltip="{{ 'NEXT_ITEM' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>last_page</mat-icon>
</button>
<button
mat-icon-button (click)="previousSlide()"
matTooltip="{{ 'PREVIOUS_SLIDE' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>navigate_before</mat-icon>
</button>
<button
mat-icon-button (click)="nextSlide()"
matTooltip="{{ 'NEXT_SLIDE' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>navigate_next</mat-icon>
</button>
<button
mat-icon-button
#squashedDisplayButton
(click)="openDisplayModeSelector()"
class="squashed-display-button"
matTooltip="{{ 'CHANGE_DISPLAY_MODE' | translate | titlecase }}"
matTooltipPosition="above">
@if (state.blank) {
<mat-icon>videocam_off</mat-icon>
}
@else if (state.theme) {
<mat-icon>wallpaper</mat-icon>
}
@else if (state.display) {
<mat-icon>desktop_windows</mat-icon>
}
@else if (state.live()) {
<mat-icon>videocam</mat-icon>
}
</button>
<button
mat-icon-button (click)="blankDisplay()"
class="displayButton"
[class.active]="state.blank"
[disabled]="state.blank"
matTooltip="{{ 'SHOW_BLACK' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>videocam_off</mat-icon>
</button>
<button
mat-icon-button
(click)="themeDisplay()"
class="displayButton"
[class.active]="state.theme"
[disabled]="state.theme"
matTooltip="{{ 'SHOW_BACKGROUND' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>wallpaper</mat-icon>
</button>
<button
mat-icon-button (click)="desktopDisplay()"
class="displayButton"
[class.active]="state.display"
[disabled]="state.display"
matTooltip="{{ 'SHOW_DESKTOP' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>desktop_windows</mat-icon>
</button>
<button
mat-icon-button (click)="showDisplay()"
class="displayButton"
[class.active]="state.display"
[disabled]="state.live()"
matTooltip="{{ 'SHOW_PRESENTATION' | translate | titlecase }}"
matTooltipPosition="above">
<mat-icon>videocam</mat-icon>
</button>
}
</mat-toolbar>
<nav *ngIf="fastSwitching"
mat-tab-nav-bar mat-stretch-tabs
class="fast-switcher"
[tabPanel]="tabPanel">
<a mat-tab-link
routerLink="/service"
routerLinkActive #serviceRoute="routerLinkActive"
[active]="serviceRoute.isActive"><mat-icon>list</mat-icon></a>
<a mat-tab-link
routerLink="/slides"
routerLinkActive #slidesRoute="routerLinkActive"
[active]="slidesRoute.isActive"><mat-icon>collections</mat-icon></a>
<a mat-tab-link
routerLink="/alerts"
routerLinkActive #alertsRoute="routerLinkActive"
[active]="alertsRoute.isActive"><mat-icon>error</mat-icon></a>
<a mat-tab-link
routerLink="/search"
routerLinkActive #searchRoute="routerLinkActive"
[active]="searchRoute.isActive"><mat-icon>search</mat-icon></a>
<a mat-tab-link
routerLink="/themes"
routerLinkActive #themesRoute="routerLinkActive"
[active]="themesRoute.isActive"><mat-icon>image</mat-icon></a>
</nav>
@if (fastSwitching) {
<nav
mat-tab-nav-bar mat-stretch-tabs
class="fast-switcher"
[tabPanel]="tabPanel">
<a mat-tab-link
routerLink="/service"
routerLinkActive #serviceRoute="routerLinkActive"
[active]="serviceRoute.isActive"><mat-icon>list</mat-icon></a>
<a mat-tab-link
routerLink="/slides"
routerLinkActive #slidesRoute="routerLinkActive"
[active]="slidesRoute.isActive"><mat-icon>collections</mat-icon></a>
<a mat-tab-link
routerLink="/alerts"
routerLinkActive #alertsRoute="routerLinkActive"
[active]="alertsRoute.isActive"><mat-icon>error</mat-icon></a>
<a mat-tab-link
routerLink="/search"
routerLinkActive #searchRoute="routerLinkActive"
[active]="searchRoute.isActive"><mat-icon>search</mat-icon></a>
<a mat-tab-link
routerLink="/themes"
routerLinkActive #themesRoute="routerLinkActive"
[active]="themesRoute.isActive"><mat-icon>image</mat-icon></a>
</nav>
}
</footer>
</mat-sidenav-content>
</mat-sidenav-container>

View File

@ -1,5 +1,5 @@
describe('AppComponent', () => {
it('has a dummy test', () => {
expect().nothing();
expect(null).toBe(null);
});
});

View File

@ -13,14 +13,14 @@ import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { DisplayModeSelectorComponent } from './components/display-mode-selector/display-mode-selector.component';
import { Shortcuts, ShortcutsService } from './shortcuts.service';
import { ShortcutPipe } from './components/shortcuts/shortcut.pipe';
import { ShortcutPipe } from './components/pipes/shortcut.pipe';
import { SettingsService } from './settings.service';
import * as supportedBrowsers from '../assets/supportedBrowsers';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
styleUrl: './app.component.scss'
})
export class AppComponent implements OnInit {
// Make DisplayMode enum visible to html code
@ -39,14 +39,11 @@ export class AppComponent implements OnInit {
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.pageTitleService.pageTitleChanged$.subscribe(pageTitle => this.pageTitle = pageTitle);
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
@ -67,6 +64,8 @@ export class AppComponent implements OnInit {
this.openlpService.getLanguage().subscribe(res => {
this.translateService.use(res.language);
});
} else {
this.translateService.use('default');
}
this.useShortcutsFromOpenlp = this.openlpService.assertApiVersionMinimum(2, 5)
this.shortcutsService.getShortcuts(this.useShortcutsFromOpenlp);

View File

@ -1,8 +1,9 @@
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BrowserModule, Title } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { TitleCasePipe } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
@ -24,11 +25,11 @@ 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';
import { TranslationService } from './translation.service';
import { WindowRef } from './window-ref.service';
import { AppRoutingModule } from './app.routing';
import { ServiceComponent } from './components/service/service.component';
@ -38,9 +39,9 @@ import { SearchOptionsComponent } from './components/search/search-options/searc
import { SlidesComponent } from './components/slides/slides.component';
import { ChordViewComponent } from './components/chord-view/chord-view.component';
import { StageViewComponent } from './components/stage-view/stage-view.component';
import { Nl2BrPipe } from './components/stage-view/nl2br.pipe';
import { Nl2BrPipe } from './components/pipes/nl2br.pipe';
import { MainViewComponent } from './components/main-view/main-view.component';
import { ChordProPipe } from './components/chord-view/chordpro.pipe';
import { ChordProPipe } from './components/pipes/chordpro.pipe';
import { LoginComponent } from './components/login/login.component';
import { ThemesComponent } from './components/themes/themes.component';
import { SlideListComponent } from './components/slides/slide-list/slide-list.component';
@ -50,14 +51,10 @@ import { ServiceListComponent } from './components/service/service-list/service-
import { ChordViewItemComponent } from './components/chord-view/chord-view-item/chord-view-item.component';
import { StageViewItemComponent } from './components/stage-view/stage-view-item/stage-view-item.component';
import { DisplayModeSelectorComponent } from './components/display-mode-selector/display-mode-selector.component';
import { SentenceCasePipe } from './components/pipes/sentence-case.pipe';
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: [
AppComponent,
@ -75,6 +72,7 @@ export function createTranslateLoader(http: HttpClient) {
AlertComponent,
SearchComponent,
SearchOptionsComponent,
SentenceCasePipe,
SlidesComponent,
SlideListComponent,
SlideItemComponent,
@ -108,18 +106,19 @@ export function createTranslateLoader(http: HttpClient) {
MatBottomSheetModule,
MatSliderModule,
TranslateModule.forRoot({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
useClass: TranslationService
}
})
],
providers: [
PageTitleService,
OpenLPService,
TranslationService,
SentenceCasePipe,
Title,
TitleCasePipe,
WindowRef
],
bootstrap: [AppComponent]

View File

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

View File

@ -1,5 +1,7 @@
import { Component } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { TitleCasePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
import { PageTitleService } from '../../page-title.service';
import { OpenLPService } from '../../openlp.service';
@ -7,20 +9,32 @@ import { OpenLPService } from '../../openlp.service';
@Component({
selector: 'openlp-alert',
templateUrl: './alert.component.html',
styleUrls: ['./alert.component.scss'],
styleUrl: './alert.component.scss',
providers: [OpenLPService]
})
export class AlertComponent {
public alert: string;
public alertMessage: string;
constructor(private pageTitleService: PageTitleService, private openlpService: OpenLPService,
private snackBar: MatSnackBar) {
pageTitleService.changePageTitle('Alerts');
constructor(
private pageTitleService: PageTitleService,
private openlpService: OpenLPService,
private snackBar: MatSnackBar,
private titleCasePipe: TitleCasePipe,
private translateService: TranslateService) {
this.translateService.stream('ALERTS').subscribe(res => {
this.pageTitleService.changePageTitle(res);
});
this.translateService.stream('ALERT_SUBMITTED').subscribe(res => {
this.alertMessage = this.titleCasePipe.transform(res);
});
}
onSubmit() {
this.openlpService.showAlert(this.alert).subscribe(() => this.snackBar.open('Alert submitted', '', {duration: 2000}));
this.openlpService.showAlert(this.alert).subscribe(
() => this.snackBar.open(this.alertMessage, '', { duration: 2000 })
);
}
}

View File

@ -1,7 +1,7 @@
<div
class="slide song"
[class.currentSlide]="active"
[class.mat-headline-2]="active"
[class.first]="!active && slide.first_slide_of_tag"
[innerHTML]="chordproFormatted(slide)|chordpro">
</div>
class="slide song"
[class.currentSlide]="active"
[class.mat-headline-2]="active"
[class.first]="!active && slide.first_slide_of_tag"
[innerHTML]="chordproFormatted(slide)|chordpro">
</div>

View File

@ -1,53 +1,74 @@
<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>
@for (tag of tags; track tag) {
<span [class.active]="tag.active">{{ tag.text }}</span>
}
</div>
<div class="container">
<app-chord-view-item
[slide]="currentSlides[activeSlide]"
[active]="true"
*ngIf="currentSlides[activeSlide]?.chords; else elseSlideNoChords"
></app-chord-view-item>
<ng-template #elseSlideNoChords>
<app-stage-view-item [slide]="currentSlides[activeSlide]" [active]="true"></app-stage-view-item>
</ng-template>
@if (currentSlides[activeSlide]?.chords) {
<app-chord-view-item
[slide]="currentSlides[activeSlide]"
[active]="true">
</app-chord-view-item>
}
@else {
<app-stage-view-item
[slide]="currentSlides[activeSlide]"
[active]="true">
</app-stage-view-item>
}
<div class="nextSlides">
<ng-container *ngFor="let slide of nextSlides; trackBy: trackByIndex">
<app-chord-view-item [slide]="slide" *ngIf="slide?.chords; else elseNextSlideNoChords"></app-chord-view-item>
<ng-template #elseNextSlideNoChords>
<app-stage-view-item [slide]="slide"></app-stage-view-item>
</ng-template>
</ng-container>
@for (slide of nextSlides; track trackByIndex) {
<ng-container>
@if (slide?.chords) {
<app-chord-view-item
[slide]="slide">
</app-chord-view-item>
}
@else {
<app-stage-view-item
[slide]="slide">
</app-stage-view-item>
}
</ng-container>
}
</div>
</div>
</div>
<div class="toolbar">
<a *ngIf="!embedded"
mat-mini-fab color=""
class="back-button"
routerLink="/"
matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate }}">
<mat-icon>arrow_back</mat-icon>
</a>
@if (!embedded) {
<a
mat-mini-fab color=""
class="back-button"
routerLink="/"
matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate | titlecase }}">
<mat-icon>arrow_back</mat-icon>
</a>
}
<div class="transpose">
<button mat-icon-button (click)="transposeDown()">
<button
mat-icon-button
(click)="transposeDown()">
<mat-icon>keyboard_arrow_down</mat-icon>
</button>
<span>{{ transposeLevel }}</span>
<button mat-icon-button (click)="transposeUp()">
<button
mat-icon-button
(click)="transposeUp()">
<mat-icon>keyboard_arrow_up</mat-icon>
</button>
</div>
<div *ngIf="!embedded && activeSlide+1 === currentSlides.length"
class="next-service-item"
matTooltip="{{ 'NEXT_ITEM' | translate }}">
{{ nextServiceItemTitle }}
</div>
@if (!embedded && activeSlide+1 === currentSlides.length) {
<div
class="next-service-item"
matTooltip="{{ 'NEXT_ITEM' | translate | titlecase }}">
{{ nextServiceItemTitle }}
</div>
}
<div class="time">{{ (openlpService.getIsTwelveHourTime()) ? (time|date:'h:mm a') : (time|date:'HH:mm') }}</div>
</div>
</div>

View File

@ -1,84 +1,98 @@
<mat-action-list *ngIf="!display.bigDisplayButtons">
<button mat-list-item
aria-labelledby="caption-blank"
class="display-button"
(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' | translate }}</span>
</button>
<button mat-list-item
aria-labelledby="caption-theme"
class="display-button"
(click)="setMode(DisplayMode.Theme)"
[disabled]="displayMode === DisplayMode.Theme">
<mat-icon class="small-icon">wallpaper</mat-icon>
<span id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate }}</span>
</button>
<button mat-list-item
aria-labelledby="caption-desktop"
class="display-button"
(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' | translate }}</span>
</button>
<button mat-list-item
aria-labelledby="caption-presentation"
class="display-button"
(click)="setMode(DisplayMode.Presentation)"
[disabled]="displayMode === DisplayMode.Presentation">
<mat-icon class="small-icon">videocam</mat-icon>
<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">
<mat-grid-tile>
<button mat-fab color="primary"
@if (display.bigDisplayButtons) {
<mat-grid-list
cols="2"
rowHeight="2:1">
<mat-grid-tile>
<button
mat-fab color="primary"
aria-labelledby="caption-blank"
class="display-button"
(click)="setMode(displayMode.Blank)"
[disabled]="display.displayMode === displayMode.Blank">
<mat-icon class="big-icon">videocam_off</mat-icon>
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate | titlecase }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button
mat-fab color="primary"
aria-labelledby="caption-theme"
class="display-button"
(click)="setMode(displayMode.Theme)"
[disabled]="display.displayMode === displayMode.Theme">
<mat-icon class="big-icon">wallpaper</mat-icon>
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate | titlecase }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button
mat-fab color="primary"
aria-labelledby="caption-desktop"
class="display-button"
(click)="setMode(displayMode.Desktop)"
[disabled]="display.displayMode === displayMode.Desktop">
<mat-icon class="big-icon">desktop_windows</mat-icon>
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate | titlecase }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button
mat-fab color="primary"
aria-labelledby="caption-presentation"
class="display-button"
(click)="setMode(displayMode.Presentation)"
[disabled]="display.displayMode === displayMode.Presentation">
<mat-icon class="big-icon">videocam</mat-icon>
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate | titlecase }}</div>
</mat-grid-tile>
</mat-grid-list>
}
@else {
<mat-action-list>
<button
mat-list-item
aria-labelledby="caption-blank"
class="display-button"
(click)="setMode(DisplayMode.Blank)"
[disabled]="displayMode === DisplayMode.Blank">
<mat-icon class="big-icon">videocam_off</mat-icon>
(click)="setMode(displayMode.Blank)"
[disabled]="display.displayMode === displayMode.Blank">
<mat-icon class="small-icon">videocam_off</mat-icon>
<span id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate | titlecase }}</span>
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button mat-fab color="primary"
<button
mat-list-item
aria-labelledby="caption-theme"
class="display-button"
(click)="setMode(DisplayMode.Theme)"
[disabled]="displayMode === DisplayMode.Theme">
<mat-icon class="big-icon">wallpaper</mat-icon>
(click)="setMode(displayMode.Theme)"
[disabled]="display.displayMode === displayMode.Theme">
<mat-icon class="small-icon">wallpaper</mat-icon>
<span id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate | titlecase }}</span>
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button mat-fab color="primary"
<button
mat-list-item
aria-labelledby="caption-desktop"
class="display-button"
(click)="setMode(DisplayMode.Desktop)"
[disabled]="displayMode === DisplayMode.Desktop">
<mat-icon class="big-icon">desktop_windows</mat-icon>
(click)="setMode(displayMode.Desktop)"
[disabled]="display.displayMode === displayMode.Desktop">
<mat-icon class="small-icon">desktop_windows</mat-icon>
<span id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate | titlecase }}</span>
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate }}</div>
</mat-grid-tile>
<mat-grid-tile>
<button mat-fab color="primary"
<button
mat-list-item
aria-labelledby="caption-presentation"
class="display-button"
(click)="setMode(DisplayMode.Presentation)"
[disabled]="displayMode === DisplayMode.Presentation">
<mat-icon class="big-icon">videocam</mat-icon>
(click)="setMode(displayMode.Presentation)"
[disabled]="display.displayMode === displayMode.Presentation">
<mat-icon class="small-icon">videocam</mat-icon>
<span id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate | titlecase }}</span>
</button>
</mat-grid-tile>
<mat-grid-tile>
<div id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate }}</div>
</mat-grid-tile>
</mat-grid-list>
</mat-action-list>
}

View File

@ -5,11 +5,11 @@ import { Display, DisplayMode } from 'src/app/responses';
@Component({
selector: 'openlp-display-mode-sheet',
templateUrl: 'display-mode-selector.component.html',
styleUrls: ['./display-mode-selector.component.scss']
styleUrl: './display-mode-selector.component.scss'
})
export class DisplayModeSelectorComponent {
// Make DisplayMode enum visible in HTML template.
DisplayMode = DisplayMode;
displayMode = DisplayMode;
constructor(
private bottomSheetRef: MatBottomSheetRef<DisplayModeSelectorComponent>,

View File

@ -1,17 +1,19 @@
<h1 mat-dialog-title>{{ 'LOGIN' | translate }}</h1>
<h1 mat-dialog-title>{{ 'LOGIN' | translate | titlecase }}</h1>
<form #loginForm="ngForm">
<div mat-dialog-content>
<mat-form-field>
<input matInput
placeholder="{{ 'USER_NAME' | translate }}"
<input
matInput
placeholder="{{ 'USER_NAME' | translate | titlecase }}"
[(ngModel)]="username"
name="username"
required>
</mat-form-field>
<mat-form-field>
<input matInput
placeholder="{{ 'PASSWORD' | translate }}"
<input
matInput
placeholder="{{ 'PASSWORD' | translate | titlecase }}"
type="password"
[(ngModel)]="password"
name="password"
@ -23,7 +25,7 @@
color="primary"
[disabled]="!loginForm.form.valid"
(click)="performLogin()">
{{ 'LOGIN' | translate }}
{{ 'LOGIN' | translate | titlecase }}
</button>
</div>
</form>

View File

@ -1,30 +1,43 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { TitleCasePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
import { OpenLPService } from '../../openlp.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
styleUrl: './login.component.scss'
})
export class LoginComponent implements OnInit {
export class LoginComponent {
username: string;
password: string;
constructor(private dialogRef: MatDialogRef<LoginComponent>, private openlpService: OpenLPService,
private snackBar: MatSnackBar) { }
ngOnInit() {
// Do nothing
loginSucceededMessage: string;
loginFailedMessage: string;
constructor(
private dialogRef: MatDialogRef<LoginComponent>,
private openlpService: OpenLPService,
private snackBar: MatSnackBar,
private titleCasePipe: TitleCasePipe,
private translateService: TranslateService) {
this.translateService.stream('LOGIN_SUCCEEDED').subscribe(res => {
this.loginSucceededMessage = this.titleCasePipe.transform(res);
});
this.translateService.stream('LOGIN_FAILED').subscribe(res => {
this.loginFailedMessage = this.titleCasePipe.transform(res);
});
}
performLogin() {
this.openlpService.login({username: this.username, password: this.password}).subscribe(
result => {
this.snackBar.open('Successfully logged in', '', {duration: 2000});
this.openlpService.login({ username: this.username, password: this.password }).subscribe({
next: result => {
this.snackBar.open(this.loginSucceededMessage, '', { duration: 2000 });
this.dialogRef.close(result);
},
() => this.snackBar.open('Login failed', '', {duration: 2000})
);
error: () => this.snackBar.open(this.loginFailedMessage, '', { duration: 2000 })
});
}
}

View File

@ -1,6 +1,9 @@
<div class="lower-third">
<div class="slide">
<div class="slide-item" [style.font-size]="fontSize" [style.font-family]="fontFamily">
<div
class="slide-item"
[style.font-size]="fontSize"
[style.font-family]="fontFamily">
{{currentSlides[activeSlide]?.text}}
</div>
</div>

View File

@ -8,7 +8,7 @@ import { ServiceItem, Slide } from '../../responses';
@Component({
selector: 'app-lower-third',
templateUrl: './lower-third.component.html',
styleUrls: ['./lower-third.component.scss'],
styleUrl: './lower-third.component.scss',
encapsulation: ViewEncapsulation.None
})
export class LowerThirdComponent implements OnInit, OnDestroy {

View File

@ -5,7 +5,7 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
export class Nl2BrPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(value: string): string|SafeHtml {
transform(value: string): string | SafeHtml {
if (!value) {
return value;
}

View File

@ -0,0 +1,17 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'sentencecase'})
export class SentenceCasePipe implements PipeTransform {
transform(value: string): string {
if (!value) {
return value;
}
if (typeof value !== 'string') {
throw Error(`Invalid pipe argument: '${value}' for pipe 'SentenceCasePipe'`);
}
const sentenceEndMarker: string = '. '
return value.split(sentenceEndMarker).map(
(sentence) => sentence = sentence.charAt(0).toUpperCase() + sentence.slice(1).toLowerCase()
).join(sentenceEndMarker);
}
}

View File

@ -1,8 +1,16 @@
<mat-form-field >
<mat-select [(ngModel)]="selectedSearchOption" (selectionChange)="setSearchOption($event)" name="selectedSearchOption" [placeholder]="searchOptionsTitle">
<mat-option *ngFor="let option of searchOptions" name="searchOptions" [value]="option">
{{option}}
</mat-option>
<mat-select
[(ngModel)]="selectedSearchOption"
(selectionChange)="setSearchOption($event)"
name="selectedSearchOption"
[placeholder]="searchOptionsTitle">
@for (option of searchOptions; track option) {
<mat-option
name="searchOptions"
[value]="option">
{{ option }}
</mat-option>
}
</mat-select>
</mat-form-field>
<br>

View File

@ -1,26 +1,33 @@
import { Component } from '@angular/core';
import { TitleCasePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
import { OpenLPService } from '../../../openlp.service';
@Component({
selector: 'openlp-search-options',
templateUrl: './search-options.component.html',
styleUrls: ['./search-options.component.scss'],
styleUrl: './search-options.component.scss',
providers: [OpenLPService]
})
export class SearchOptionsComponent {
public selectedPlugin: string;
public searchOptions: Array<string>;
public selectedSearchOption: string;
public searchOptionsTitle = '';
public searchOptionsTitle: string;
constructor(private openlpService: OpenLPService) {}
constructor(
private openlpService: OpenLPService,
private titleCasePipe: TitleCasePipe,
private translateService: TranslateService) { }
// Used to display search-options for certain plugins
onPluginChange(plugin) {
this.selectedPlugin = plugin;
if (this.selectedPlugin === 'bibles') {
this.searchOptionsTitle = 'Bible version:';
this.translateService.stream('BIBLE_VERSION').subscribe(res => {
this.searchOptionsTitle = this.titleCasePipe.transform(res) + ':';
});
this.getSearchOptions();
}
}

View File

@ -1,10 +1,18 @@
<h3>Search</h3>
<h3>{{ 'SEARCH' | translate | titlecase }}</h3>
<form #searchForm="ngForm">
<mat-form-field>
<mat-select [(ngModel)]="selectedPlugin" (selectionChange)="onPluginChange()" name="selectedPlugin" placeholder="Search for:">
<mat-option *ngFor="let plugin of searchPlugins" name="searchPlugins" [value]="plugin.key">
{{plugin.name}}
</mat-option>
<mat-select
[(ngModel)]="selectedPlugin"
(selectionChange)="onPluginChange()"
name="selectedPlugin"
placeholder="Search for:">
@for (plugin of searchPlugins; track plugin.name) {
<mat-option
name="searchPlugins"
[value]="plugin.key">
{{ plugin.name }}
</mat-option>
}
</mat-select>
</mat-form-field>
<br>
@ -12,36 +20,53 @@
<openlp-search-options></openlp-search-options>
</div>
<mat-form-field>
<input matInput [(ngModel)]="searchText" name="searchText" placeholder="{{ 'SEARCH_TEXT' | translate }}" required>
<input
matInput
[(ngModel)]="searchText"
name="searchText"
placeholder="{{ 'SEARCH_TEXT' | translate | titlecase }}"
required>
</mat-form-field>
<br>
<button mat-raised-button id="searchButton"
<button
mat-raised-button
id="searchButton"
color="primary"
[disabled]="!searchForm.form.valid"
(click)="onSubmit()">
{{ 'SEARCH' | translate }}
{{ 'SEARCH' | translate | titlecase }}
</button>
</form>
<div *ngIf="searchResults">
<h3>{{ 'SEARCH_RESULTS' | translate }}:</h3>
<div *ngIf="!searchResults.length">
{{ 'NO_SEARCH_RESULTS' | translate }}...
@if (searchResults) {
<div>
<h3>{{ 'SEARCH_RESULTS' | translate | titlecase }}:</h3>
@if (searchResults.length) {
<table>
@for (item of searchResults; track item) {
<tr>
<td>{{ item[1] }}</td>
<td>
<button
mat-button color="primary"
(click)="addToService(item[0])">
{{ 'ADD_TO_SERVICE' | translate | sentencecase }}
</button>
</td>
<td>
<button
mat-button color="accent"
(click)="sendLive(item[0])">
{{ 'SEND_LIVE' | translate | sentencecase }}
</button>
</td>
</tr>
}
</table>
}
@else {
<div>
{{ 'NO_SEARCH_RESULTS' | translate | sentencecase }}...
</div>
}
</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' | translate }}
</button>
</td>
<td>
<button mat-button color="accent"
(click)="sendLive(item[0])">
{{ 'SEND_LIVE' | translate }}
</button>
</td>
</tr>
</table>
</div>
}

View File

@ -1,4 +1,5 @@
import { AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { OpenLPService } from '../../openlp.service';
import { PageTitleService } from '../../page-title.service';
@ -8,7 +9,7 @@ import { SearchOptionsComponent } from './search-options/search-options.componen
@Component({
selector: 'openlp-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss'],
styleUrl: './search.component.scss',
providers: [OpenLPService]
})
export class SearchComponent implements OnInit, AfterViewInit {
@ -20,9 +21,14 @@ export class SearchComponent implements OnInit, AfterViewInit {
public displaySearchOptions = false;
@ViewChild(SearchOptionsComponent, {static: false}) searchOptions: SearchOptionsComponent;
constructor(private pageTitleService: PageTitleService, private openlpService: OpenLPService,
private cdr: ChangeDetectorRef) {
pageTitleService.changePageTitle('Search');
constructor(
private pageTitleService: PageTitleService,
private openlpService: OpenLPService,
private cdr: ChangeDetectorRef,
private translateService: TranslateService) {
this.translateService.stream('SEARCH').subscribe(res => {
this.pageTitleService.changePageTitle(res);
});
}
onSubmit() {

View File

@ -1,4 +1,7 @@
<mat-card (click)="onItemSelected(item)" class="service-item no-select" [class.selected]="selected">
<mat-card
(click)="onItemSelected(item)"
class="service-item no-select"
[class.selected]="selected">
<mat-card-content>
<mat-icon>{{ getIcon(item) }}</mat-icon> {{ item.title }}
</mat-card-content>

View File

@ -5,7 +5,7 @@ import { ServiceItem } from '../../../responses';
@Component({
selector: 'openlp-service-item',
templateUrl: './service-item.component.html',
styleUrls: ['./service-item.component.scss'],
styleUrl: './service-item.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})

View File

@ -1,22 +1,29 @@
<ng-container *ngIf="items?.length; else noServiceItems">
<openlp-service-item
*ngFor="let item of items"
[item]="item"
[selected]="item.selected"
(selectItem)="onItemSelected($event)"
[tabindex]="item.id"></openlp-service-item>
</ng-container>
<ng-template #noServiceItems>
<div class="no-items" *ngIf="!loading">
@if (items?.length) {
<ng-container>
@for (item of items; track item) {
<openlp-service-item
[item]="item"
[selected]="item.selected"
(selectItem)="onItemSelected($event)"
[tabindex]="item.id">
</openlp-service-item>
}
</ng-container>
}
@else if (!loading) {
<div class="no-items">
<div class="no-items-title">
<span class="material-icons icon">info</span>
{{ 'NO_SERVICE_ITEMS' | translate }}.
{{ 'NO_SERVICE_ITEMS' | translate | sentencecase }}.
</div>
<div class="no-items-actions">
<a routerLink="/search" mat-stroked-button color="primary" size="small">
<a
routerLink="/search"
mat-stroked-button color="primary"
size="small">
<span class="material-icons">add</span>
{{ 'ADD_ITEM' | translate }}
{{ 'ADD_ITEM' | translate | sentencecase }}
</a>
</div>
</div>
</ng-template>
}

View File

@ -7,7 +7,7 @@ import { ServiceItem } from '../../../responses';
@Component({
selector: 'openlp-service-list',
templateUrl: './service-list.component.html',
styleUrls: ['./service-list.component.scss', '../../no-items.scss'],
styleUrls: ['./service-list.component.scss', '../../no-items.scss']
})
export class ServiceListComponent implements OnInit, OnDestroy {

View File

@ -1 +1,3 @@
<openlp-service-list (itemSelected)="this.onItemSelected($event)"></openlp-service-list>
<openlp-service-list
(itemSelected)="this.onItemSelected($event)">
</openlp-service-list>

View File

@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { OpenLPService } from '../../openlp.service';
import { PageTitleService } from '../../page-title.service';
@ -8,7 +9,7 @@ import { ServiceItem } from '../../responses';
@Component({
selector: 'openlp-service',
templateUrl: './service.component.html',
styleUrls: ['./service.component.scss'],
styleUrl: './service.component.scss',
})
export class ServiceComponent {
@ -19,8 +20,13 @@ export class ServiceComponent {
}
}
constructor(protected pageTitleService: PageTitleService, protected openlpService: OpenLPService,
protected router: Router) {
pageTitleService.changePageTitle('Service');
constructor(
protected pageTitleService: PageTitleService,
protected openlpService: OpenLPService,
protected router: Router,
private translateService: TranslateService) {
this.translateService.stream('SERVICE').subscribe(res => {
this.pageTitleService.changePageTitle(res);
});
}
}

View File

@ -1,64 +1,70 @@
<div class="settings-panel">
<mat-card>
<mat-card-header>
{{ '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' | 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' | translate }}
</mat-slide-toggle>
</div>
</mat-card-content>
<mat-card>
<mat-card-header>
{{ 'USER_INTERFACE' | translate | titlecase }}
</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' | translate | sentencecase }}
</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' | translate | sentencecase }}
</mat-slide-toggle>
</div>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-header>
{{ 'STAGE_AND_CHORDS_APPEARANCE' | translate }}
</mat-card-header>
<mat-card-content>
<mat-tab-group>
<mat-tab label="{{ 'STAGE' | translate }}">
<ng-template matTabContent>
<ng-container>
<openlp-stage-chord-preview stageType="stage"></openlp-stage-chord-preview>
<ng-container *ngTemplateOutlet="stageSettings; context: {prefix: 'stage'}"></ng-container>
</ng-container>
</ng-template>
</mat-tab>
<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-template>
</mat-tab>
</mat-tab-group>
</mat-card-content>
<mat-card-header>
{{ 'STAGE_AND_CHORDS_APPEARANCE' | translate | sentencecase }}
</mat-card-header>
<mat-card-content>
<mat-tab-group>
<mat-tab label="{{ 'STAGE' | translate | titlecase }}">
<ng-template matTabContent>
<ng-container>
<openlp-stage-chord-preview stageType="stage">
</openlp-stage-chord-preview>
<ng-container *ngTemplateOutlet="stageSettings; context: {prefix: 'stage'}">
</ng-container>
</ng-container>
</ng-template>
</mat-tab>
<mat-tab label="{{ 'CHORDS' | translate | titlecase }}">
<ng-template matTabContent>
<openlp-stage-chord-preview stageType="chords">
</openlp-stage-chord-preview>
<ng-container *ngTemplateOutlet="stageSettings; context: {prefix: 'chords'}">
</ng-container>
</ng-template>
</mat-tab>
</mat-tab-group>
</mat-card-content>
</mat-card>
</div>
<ng-template #stageSettings let-prefix="prefix">
<div class="stage-settings">
<div class="settings-item">
<label>{{ 'FONT_SCALE' | translate }}: {{settings[prefix + 'FontScale'] ?? 100}}%</label>
<mat-slider
min="25"
max="200"
step="6.25">
<input
matSliderThumb
[value]="settings[prefix + 'FontScale']"
(valueChange)="setSetting(prefix + 'FontScale', $event)">
</mat-slider>
</div>
<ng-template
#stageSettings
let-prefix="prefix">
<div class="stage-settings">
<div class="settings-item">
<label>{{ 'FONT_SCALE' | translate | sentencecase }}: {{settings[prefix + 'FontScale'] ?? 100}}%</label>
<mat-slider
min="25"
max="200"
step="6.25">
<input
matSliderThumb
[value]="settings[prefix + 'FontScale']"
(valueChange)="setSetting(prefix + 'FontScale', $event)">
</mat-slider>
</div>
</div>
</ng-template>

View File

@ -1,4 +1,5 @@
import { Component, OnDestroy } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { OpenLPService } from '../../openlp.service';
import { PageTitleService } from '../../page-title.service';
@ -6,17 +7,19 @@ import { SettingsProperties, SettingsPropertiesItem, SettingsService } from '../
@Component({
selector: 'openlp-settings',
templateUrl: `./settings.component.html`,
styleUrls: [`./settings.component.scss`]
templateUrl: './settings.component.html',
styleUrl: './settings.component.scss'
})
export class SettingsComponent implements OnDestroy {
constructor(
protected pageTitleService: PageTitleService,
protected openlpService: OpenLPService,
protected settingsService: SettingsService,
) {
private translateService: TranslateService) {
this.settingsSubscription$ = settingsService.settingChanged$.subscribe(this._settingChanged);
pageTitleService.changePageTitle('Settings');
this.translateService.stream('SETTINGS').subscribe(res => {
this.pageTitleService.changePageTitle(res);
});
}
protected settingsSubscription$: Subscription;
@ -36,5 +39,4 @@ export class SettingsComponent implements OnDestroy {
ngOnDestroy(): void {
this.settingsSubscription$.unsubscribe();
}
}

View File

@ -1,14 +1,18 @@
<div class="stage-preview-container" #stageViewContainer>
<div
class="stage-preview-container"
#stageViewContainer>
@if (stageType === 'stage') {
<app-stage-view
#stageView
*ngIf="stageType === 'stage'"
[embedded]="true"
[style.--openlp-stage-font-scale]="fontScale"
></app-stage-view>
#stageView
[embedded]="true"
[style.--openlp-stage-font-scale]="fontScale">
</app-stage-view>
}
@else if (stageType === 'chords') {
<app-chord-view
#chordsView
*ngIf="stageType === 'chords'"
[embedded]="true"
[style.--openlp-stage-font-scale]="fontScale"
></app-chord-view>
#chordsView
[embedded]="true"
[style.--openlp-stage-font-scale]="fontScale">
</app-chord-view>
}
</div>

View File

@ -17,7 +17,7 @@ import { SettingsProperties, SettingsService } from 'src/app/settings.service';
@Component({
selector: 'openlp-stage-chord-preview',
templateUrl: './stage-chord-preview.component.html',
styleUrls: ['./stage-chord-preview.component.scss'],
styleUrl: './stage-chord-preview.component.scss',
})
export class StageChordPreviewComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {
constructor(

View File

@ -1,14 +1,20 @@
<mat-card class="slide no-select" mat-list-item (click)="onSlideSelected(slide)" [class.selected]="selected">
<mat-card
class="slide no-select"
mat-list-item
(click)="onSlideSelected(slide)"
[class.selected]="selected">
<mat-card-content>
<div class="verse-tag">{{ slide?.tag }}</div>
<div *ngIf="slide?.img; else onlySlideText" class="verse-img-container">
<img src="{{ slide?.img }}" />
<div class="img-verse-text">
{{ slide?.text }}
@if (slide?.img) {
<div class="verse-img-container">
<img src="{{ slide?.img }}" />
<div class="img-verse-text">
{{ slide?.text }}
</div>
</div>
</div>
<ng-template #onlySlideText>
}
@else {
<div class="verse-text">{{ slide?.text }}</div>
</ng-template>
}
</mat-card-content>
</mat-card>

View File

@ -4,7 +4,7 @@ import { Slide } from '../../../responses';
@Component({
selector: 'openlp-slide-item',
templateUrl: './slide-item.component.html',
styleUrls: ['./slide-item.component.scss'],
styleUrl: './slide-item.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})

View File

@ -1,23 +1,32 @@
<ng-container *ngIf="slides?.length; else noSlideItems">
<openlp-slide-item
*ngFor="let slide of slides; let index = index"
[slide]="slide"
[tabindex]="counter"
[selected]="slide.selected"
(selectSlide)="onSlideSelected($event, index)"
></openlp-slide-item>
</ng-container>
<ng-template #noSlideItems>
<div class="no-items" *ngIf="!loading">
<div class="no-items-title">
<span class="material-icons icon">info</span>
{{ 'NO_SLIDE_ITEMS' | translate }}.
@if (slides?.length) {
<ng-container>
@for (slide of slides; track slide; let index = $index) {
<openlp-slide-item
[slide]="slide"
[tabindex]="index"
[selected]="slide.selected"
(selectSlide)="onSlideSelected($event, index)">
</openlp-slide-item>
}
</ng-container>
}
@else {
@if (!loading) {
<div class="no-items">
<div class="no-items-title">
<span class="material-icons icon">info</span>
{{ 'NO_SLIDE_ITEMS' | translate | sentencecase }}.
</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' | translate | sentencecase }}
</a>
</div>
</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' | translate }}
</a>
</div>
</div>
</ng-template>
}
}

View File

@ -1 +1,2 @@
<openlp-slide-list (slideSelected)="onSlideSelected($event)"></openlp-slide-list>
<openlp-slide-list (slideSelected)="onSlideSelected($event)">
</openlp-slide-list>

View File

@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { OpenLPService } from '../../openlp.service';
import { PageTitleService } from '../../page-title.service';
@ -7,12 +8,17 @@ import { SlideListItem } from './slide-list/slide-list.component';
@Component({
selector: 'openlp-slides',
templateUrl: './slides.component.html',
styleUrls: ['./slides.component.scss'],
styleUrl: './slides.component.scss',
})
export class SlidesComponent {
constructor(protected pageTitleService: PageTitleService, protected openlpService: OpenLPService) {
pageTitleService.changePageTitle('Slides');
constructor(
protected pageTitleService: PageTitleService,
protected openlpService: OpenLPService,
private translateService: TranslateService) {
this.translateService.stream('SLIDES').subscribe(res => {
this.pageTitleService.changePageTitle(res);
});
}
onSlideSelected(item: SlideListItem) {

View File

@ -1,14 +1,21 @@
<div
class="slide"
[class.mat-headline-2]="active"
[class.currentSlide]="active"
[class.mat-headline-4]="!active"
[class.first]="!active && slide.first_slide_of_tag">
<ng-container *ngIf="!(slide?.img); else elseImage">
{{slide?.text}}
class="slide"
[class.mat-headline-2]="active"
[class.currentSlide]="active"
[class.mat-headline-4]="!active"
[class.first]="!active && slide.first_slide_of_tag">
@if (slide?.img) {
<img
src="{{slide?.img}}"
[class.active-slide-img]="active"
[class.next-slides-img]="!active"/>
<div
[class.active-slide-img-text]="active"
[class.next-slides-text]="!active">{{ slide?.text }}</div>
}
@else {
<ng-container>
{{ slide?.text }}
</ng-container>
<ng-template #elseImage>
<img src="{{slide?.img}}" [class.active-slide-img]="active" [class.next-slides-img]="!active"/>
<div [class.active-slide-img-text]="active" [class.next-slides-text]="!active">{{ slide?.text }}</div>
</ng-template>
}
</div>

View File

@ -4,49 +4,67 @@
[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>
@for (tag of tags; track tag) {
<span [class.active]="tag.active">{{ tag.text }}</span>
}
</div>
<div class="container">
<app-stage-view-item [slide]="currentSlides[activeSlide]" [active]="true"></app-stage-view-item>
<app-stage-view-item
[slide]="currentSlides[activeSlide]"
[active]="true">
</app-stage-view-item>
<div class="nextSlides">
<app-stage-view-item [slide]="slide" *ngFor="let slide of nextSlides; trackBy: trackByIndex"></app-stage-view-item>
@for (slide of nextSlides; track trackByIndex) {
<app-stage-view-item [slide]="slide">
</app-stage-view-item>
}
</div>
</div>
</div>
<div class="toolbar">
<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 *ngIf="showNotes"
mat-mini-fab
class="show-notes"
matTooltip="{{ 'HIDE_NOTES' | translate }}"
[color]="primary"
[class.show-notes-disabled]="false"
(click)="showNotes = false">
<mat-icon>sticky_note_2</mat-icon>
</button>
<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>
@if (!embedded) {
<a
class="back-button"
mat-mini-fab color=""
routerLink="/"
matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate | titlecase }}">
<mat-icon>arrow_back</mat-icon>
</a>
}
@if (showNotes) {
<button
mat-mini-fab
class="show-notes"
matTooltip="{{ 'HIDE_NOTES' | translate | titlecase }}"
color="primary"
[class.show-notes-disabled]="false"
(click)="showNotes = false">
<mat-icon>sticky_note_2</mat-icon>
</button>
}
@else {
<button
mat-mini-fab
class="show-notes"
matTooltip="{{ 'SHOW_NOTES' | translate | titlecase }}"
color=""
[class.show-notes-disabled]="true"
(click)="showNotes = true">
<mat-icon>sticky_note_2</mat-icon>
</button>
}
@if (!embedded && activeSlide+1 === currentSlides.length) {
<div
class="next-service-item"
matTooltip="{{ 'NEXT_ITEM' | translate | titlecase }}">
{{ nextServiceItemTitle }}
</div>
}
<div class="time">{{ (openlpService.getIsTwelveHourTime()) ? (time|date:'h:mm a') : (time|date:'HH:mm') }}</div>
</div>
<div class="sidebar" *ngIf="(showNotes || embedded) && notes">
<div class="notes" [innerHTML]="notes|nl2br"></div>
</div>
@if ((showNotes || embedded) && notes) {
<div class="sidebar">
<div class="notes" [innerHTML]="notes|nl2br"></div>
</div>
}
</div>

View File

@ -97,7 +97,7 @@ export class StageViewComponent implements OnInit, OnDestroy {
}
setNewSlides(slides: Slide[], _currentSlide: number): void { /* eslint-disable-line @typescript-eslint/no-unused-vars */
if (slides.length === 0) {
if ((slides?.length ?? 0) === 0) {
return;
}
this.currentSlides = slides;

View File

@ -1,26 +1,35 @@
<form #themeForm="ngForm">
<h4>{{ 'THEME_OPTIONS' | translate }}</h4>
<h4>{{ 'THEME_OPTIONS' | translate | titlecase }}</h4>
<div>
<mat-form-field>
<mat-label>{{ 'THEME_LEVEL' | translate }}</mat-label>
<mat-label>{{ 'THEME_LEVEL' | translate | titlecase }}</mat-label>
<mat-select [(value)]="themeLevel">
<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-option value="global">{{ 'GLOBAL' | translate | titlecase }}</mat-option>
<mat-option value="service">{{ 'SERVICE' | translate | titlecase }}</mat-option>
<mat-option value="song">{{ 'SONG' | translate | titlecase }}</mat-option>
</mat-select>
</mat-form-field>
</div>
<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">
<mat-card-content>
<img [src]="theme.thumbnail"/>
<div class="theme-title">{{ theme.name }}</div>
</mat-card-content>
</mat-card>
@if (isThemeLevelSupported()) {
<div class="theme-container content">
@for (theme of themeList; track theme) {
<div>
<mat-card
class="theme-card"
(click)='setTheme(theme.name)'
[class.selected]="theme.selected">
<mat-card-content>
<img [src]="theme.thumbnail"/>
<div class="theme-title">{{ theme.name }}</div>
</mat-card-content>
</mat-card>
</div>
}
</div>
</div>
<mat-error *ngIf="!isThemeLevelSupported()">
{{ 'SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED' | translate }}
</mat-error>
}
@else {
<mat-error>
{{ 'SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED' | translate | sentencecase }}
</mat-error>
}
</form>

View File

@ -1,11 +1,14 @@
import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { OpenLPService } from '../../openlp.service';
import { PageTitleService } from '../../page-title.service';
import { Theme } from '../../responses';
@Component({
selector: 'openlp-themes',
templateUrl: './themes.component.html',
styleUrls: ['./themes.component.scss'],
styleUrl: './themes.component.scss',
providers: [OpenLPService]
})
export class ThemesComponent implements OnInit {
@ -13,8 +16,13 @@ export class ThemesComponent implements OnInit {
private _themeList = [];
private _themeLevel = null;
constructor(private pageTitleService: PageTitleService, private openlpService: OpenLPService) {
pageTitleService.changePageTitle('Themes');
constructor(
private pageTitleService: PageTitleService,
private openlpService: OpenLPService,
private translateService: TranslateService) {
this.translateService.stream('THEMES').subscribe(res => {
this.pageTitleService.changePageTitle(res);
});
}
ngOnInit() {
@ -22,7 +30,7 @@ export class ThemesComponent implements OnInit {
this.getThemes();
}
get themeList(): Array<string> {
get themeList(): Array<Theme> {
return this._themeList;
}

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { TitleCasePipe } from '@angular/common';
import { Subject } from 'rxjs';
@Injectable()
@ -7,10 +8,12 @@ export class PageTitleService {
private pageTitleSource = new Subject<string>();
public pageTitleChanged$ = this.pageTitleSource.asObservable();
constructor(private titleService: Title) {}
constructor(
private titleService: Title,
private titleCasePipe: TitleCasePipe) {}
changePageTitle(pageTitle: string) {
this.pageTitleSource.next(pageTitle);
this.titleService.setTitle(pageTitle + ' | OpenLP Remote');
this.titleService.setTitle(this.titleCasePipe.transform(pageTitle) + ' | OpenLP Remote');
}
}

View File

@ -63,8 +63,9 @@ export interface ServiceItem {
}
export interface Theme {
selected: boolean;
name: string;
selected: boolean;
thumbnail: object;
}
export interface Language {

View File

@ -0,0 +1,15 @@
import { TranslateLoader } from '@ngx-translate/core';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class TranslationService implements TranslateLoader {
constructor(private http: HttpClient) {}
getTranslation(language: string): Observable<any> {
return this.http.get(language === 'default' ? '/assets/en.json' : '/assets/i18n/' + language + '.json');
}
}

View File

@ -1,5 +1,4 @@
import { TestBed } from '@angular/core/testing';
import { WindowRef} from './window-ref.service';
describe('WindowRef', () => {

57
src/assets/en.json Normal file
View File

@ -0,0 +1,57 @@
{
"_COMMENT": "This file located here instead of in the i18n directory is a workaround for Web API 2.4 or older to prevent HTTP 404 errors while retrieving the en.json file when using Web Remote 0.9.16 or newer.",
"ADD_ITEM": "Add Item",
"ADD_ITEM_TO_SERVICE": "Add Item to Service",
"ADD_TO_SERVICE": "Add to Service",
"ALERT": "Alert",
"ALERT_SUBMITTED": "Alert Submitted",
"ALERTS": "Alerts",
"BIBLE_VERSION": "Bible version",
"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",
"LOGIN_FAILED": "Login failed",
"LOGIN_SUCCEEDED": "Successfully logged in",
"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"
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Voeg Item By Diens",
"ADD_TO_SERVICE": "Voeg By Diens",
"ALERT": "Waarskuwing",
"ALERT_SUBMITTED": "Waarskuwing Ingedien",
"ALERTS": "Waarskuwings",
"BIBLE_VERSION": "Bybel weergawe",
"CHANGE_DISPLAY_MODE": "Verander Vertoonmodus",
"CHORD_VIEW": "Akkoordaansig",
"CHORDS": "Akkoorde",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Gaan Terug na Kontroleerder",
"HIDE_NOTES": "Versteek Notas",
"LOGIN": "Teken In",
"LOGIN_FAILED": "Aanmelding het misluk",
"LOGIN_SUCCEEDED": "Suksesvol aangemeld",
"MAIN_VIEW": "Hoofaansig",
"NEXT_ITEM": "Volgende Item",
"NEXT_SLIDE": "Volgende Skyfie",
@ -30,7 +34,7 @@
"SEARCH_TEXT": "Soek Teks",
"SEND": "Stuur",
"SEND_AN_ALERT": "Stuur 'n Waarskuwing",
"SEND_LIVE": "Stuur Live",
"SEND_LIVE": "Stuur Regstreeks",
"SERVICE": "Diens",
"SETTINGS": "Instellings",
"SHOW_BACKGROUND": "Wys Agtergrond",
@ -39,9 +43,9 @@
"SHOW_NOTES": "Wys Notas",
"SHOW_PRESENTATION": "Wys Voorstelling",
"SLIDES": "Skyfies",
"SONG": "Liedjie",
"SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED": "Verandering van liedjievlaktema word nie ondersteun nie. Verander jou temavlak na Global of Diens",
"STAGE": "Stage",
"SONG": "Lied",
"SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED": "Verandering van liedvlaktema word nie ondersteun nie. Verander jou temavlak na Globaal of Diens",
"STAGE": "Verhoog",
"STAGE_AND_CHORDS_APPEARANCE": "Verhoog en Akkoorde Voorkoms",
"STAGE_VIEW": "Verhoogaansig",
"THEMES": "Temas",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Tema Opsies",
"USER_NAME": "Gebruikersnaam",
"USER_INTERFACE": "Gebruikerskoppelvlak"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Добави елемент към услугата",
"ADD_TO_SERVICE": "Добави към услугата",
"ALERT": "Предупреждение",
"ALERT_SUBMITTED": "Подадено предупреждение",
"ALERTS": "Предупреждения",
"BIBLE_VERSION": "Библейска версия",
"CHANGE_DISPLAY_MODE": "Промени режима на дисплея",
"CHORD_VIEW": "Преглед на акорди",
"CHORDS": "Акорди",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Върни се към контролера",
"HIDE_NOTES": "Скрий бележките",
"LOGIN": "Вход",
"LOGIN_FAILED": "Неуспешно влизане",
"LOGIN_SUCCEEDED": "Успешно влизане",
"MAIN_VIEW": "Основен изглед",
"NEXT_ITEM": "Следващ елемент",
"NEXT_SLIDE": "Следващ слайд",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Опции за темата",
"USER_NAME": "Потребителско име",
"USER_INTERFACE": "Потребителски интерфейс"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Přidat Položku do Služby",
"ADD_TO_SERVICE": "Přidat do Služby",
"ALERT": "Upozornění",
"ALERT_SUBMITTED": "Výstraha Odeslána",
"ALERTS": "Upozornění",
"BIBLE_VERSION": "Biblická verze",
"CHANGE_DISPLAY_MODE": "Změnit Režim Zobrazení",
"CHORD_VIEW": "Zobrazení Akordů",
"CHORDS": "Akordy",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Vrátit se Zpět k Ovladači",
"HIDE_NOTES": "Skrýt Poznámky",
"LOGIN": "Přihlásit Se",
"LOGIN_FAILED": "Přihlášení se nezdařilo",
"LOGIN_SUCCEEDED": "Úspěšně přihlášeno",
"MAIN_VIEW": "Hlavní Zobrazení",
"NEXT_ITEM": "Další Položka",
"NEXT_SLIDE": "Další Snímek",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Možnosti Tématu",
"USER_NAME": "Uživatelské Jméno",
"USER_INTERFACE": "Uživatelské Rozhraní"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Tilføj Emne til Tjeneste",
"ADD_TO_SERVICE": "Tilføj til Tjeneste",
"ALERT": "Advarsel",
"ALERT_SUBMITTED": "Advarsel Indsendt",
"ALERTS": "Advarsler",
"BIBLE_VERSION": "Bibelsk udgave",
"CHANGE_DISPLAY_MODE": "Skift Visningstilstand",
"CHORD_VIEW": "Akkordvisning",
"CHORDS": "Akkorder",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Gå Tilbage til Controller",
"HIDE_NOTES": "Skjul Noter",
"LOGIN": "Log Ind",
"LOGIN_FAILED": "Login mislykkedes",
"LOGIN_SUCCEEDED": "Logget ind",
"MAIN_VIEW": "Hovedvisning",
"NEXT_ITEM": "Næste Emne",
"NEXT_SLIDE": "Næste Dias",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Temaindstillinger",
"USER_NAME": "Brugernavn",
"USER_INTERFACE": "Brugergrænseflade"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Element zum Ablauf Hinzufügen",
"ADD_TO_SERVICE": "Zum Ablauf Hinzufügen",
"ALERT": "Hinweise",
"ALERT_SUBMITTED": "Hinweise Übermittelt",
"ALERTS": "Hinweisen",
"BIBLE_VERSION": "Bibelübersetzung",
"CHANGE_DISPLAY_MODE": "Anzeigemodus Ändern",
"CHORD_VIEW": "Akkordansicht",
"CHORDS": "Akkorde",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Zurück zum Controller",
"HIDE_NOTES": "Notizen Ausblenden",
"LOGIN": "Anmelden",
"LOGIN_FAILED": "Anmeldung fehlgeschlagen",
"LOGIN_SUCCEEDED": "Erfolgreich eingeloggt",
"MAIN_VIEW": "Hauptansicht",
"NEXT_ITEM": "Nächstes Element",
"NEXT_SLIDE": "Nächste Folie",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Themenoptionen",
"USER_NAME": "Benutzername",
"USER_INTERFACE": "Benutzeroberfläche"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Προσθήκη στοιχείου στην υπηρεσία",
"ADD_TO_SERVICE": "Προσθήκη στην υπηρεσία",
"ALERT": "Ειδοποίηση",
"ALERT_SUBMITTED": "Η ειδοποίηση υποβλήθηκε",
"ALERTS": "Ειδοποιήσεις",
"BIBLE_VERSION": "Βιβλική έκδοση",
"CHANGE_DISPLAY_MODE": "Αλλαγή λειτουργίας εμφάνισης",
"CHORD_VIEW": "Προβολή συγχορδιών",
"CHORDS": "Συγχορδίες",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Επιστροφή στον ελεγκτή",
"HIDE_NOTES": "Απόκρυψη σημειώσεων",
"LOGIN": "Σύνδεση",
"LOGIN_FAILED": "Η σύνδεση απέτυχε",
"LOGIN_SUCCEEDED": "Συνδέθηκε με επιτυχία",
"MAIN_VIEW": "Κύρια προβολή",
"NEXT_ITEM": "Επόμενο στοιχείο",
"NEXT_SLIDE": "Επόμενη διαφάνεια",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Επιλογές Θέματος",
"USER_NAME": "Όνομα Χρήστη",
"USER_INTERFACE": "Διεπαφή Χρήστη"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Add Item to Service",
"ADD_TO_SERVICE": "Add to Service",
"ALERT": "Alert",
"ALERT_SUBMITTED": "Alert Submitted",
"ALERTS": "Alerts",
"BIBLE_VERSION": "Bible version",
"CHANGE_DISPLAY_MODE": "Change Display Mode",
"CHORD_VIEW": "Chord View",
"CHORDS": "Chords",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Go Back to Controller",
"HIDE_NOTES": "Hide Notes",
"LOGIN": "Login",
"LOGIN_FAILED": "Login failed",
"LOGIN_SUCCEEDED": "Successfully logged in",
"MAIN_VIEW": "Main View",
"NEXT_ITEM": "Next Item",
"NEXT_SLIDE": "Next Slide",

View File

@ -0,0 +1,56 @@
{
"ADD_ITEM": "Add Item",
"ADD_ITEM_TO_SERVICE": "Add Item to Service",
"ADD_TO_SERVICE": "Add to Service",
"ALERT": "Alert",
"ALERT_SUBMITTED": "Alert Submitted",
"ALERTS": "Alerts",
"BIBLE_VERSION": "Bible version",
"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",
"LOGIN_FAILED": "Login failed",
"LOGIN_SUCCEEDED": "Successfully logged in",
"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"
}

View File

@ -0,0 +1,56 @@
{
"ADD_ITEM": "Add Item",
"ADD_ITEM_TO_SERVICE": "Add Item to Service",
"ADD_TO_SERVICE": "Add to Service",
"ALERT": "Alert",
"ALERT_SUBMITTED": "Alert Submitted",
"ALERTS": "Alerts",
"BIBLE_VERSION": "Bible version",
"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",
"LOGIN_FAILED": "Login failed",
"LOGIN_SUCCEEDED": "Successfully logged in",
"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"
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Agregar Elemento al Servicio",
"ADD_TO_SERVICE": "Agregar al Servicio",
"ALERT": "Alerta",
"ALERT_SUBMITTED": "Alerta Enviada",
"ALERTS": "Alertas",
"BIBLE_VERSION": "Versión de la Biblia",
"CHANGE_DISPLAY_MODE": "Cambiar Modo de Visualización",
"CHORD_VIEW": "Vista de Acordes",
"CHORDS": "Acordes",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Volver al Controlador",
"HIDE_NOTES": "Ocultar Notas",
"LOGIN": "Iniciar Sesión",
"LOGIN_FAILED": "Error de inicio de sesión",
"LOGIN_SUCCEEDED": "Ha iniciado sesión correctamente",
"MAIN_VIEW": "Vista Principal",
"NEXT_ITEM": "Siguiente Elemento",
"NEXT_SLIDE": "Siguiente Diapositiva",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opciones de Tema",
"USER_NAME": "Nombre de Usuario",
"USER_INTERFACE": "Interfaz de Usuario"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Agregar Elemento al Servicio",
"ADD_TO_SERVICE": "Agregar al Servicio",
"ALERT": "Alerta",
"ALERT_SUBMITTED": "Alerta Enviada",
"ALERTS": "Alertas",
"BIBLE_VERSION": "Versión de la Biblia",
"CHANGE_DISPLAY_MODE": "Cambiar Modo de Visualización",
"CHORD_VIEW": "Vista de Acordes",
"CHORDS": "Acordes",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Volver al Controlador",
"HIDE_NOTES": "Ocultar Notas",
"LOGIN": "Iniciar Sesión",
"LOGIN_FAILED": "Error de inicio de sesión",
"LOGIN_SUCCEEDED": "Ha iniciado sesión correctamente",
"MAIN_VIEW": "Vista Principal",
"NEXT_ITEM": "Siguiente Elemento",
"NEXT_SLIDE": "Siguiente Diapositiva",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opciones de Tema",
"USER_NAME": "Nombre de Usuario",
"USER_INTERFACE": "Interfaz de Usuario"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Üksuse Lisamine Teenusesse",
"ADD_TO_SERVICE": "Lisa Teenusesse",
"ALERT": "Teatise",
"ALERT_SUBMITTED": "Hoiatusteade on Esitatud",
"ALERTS": "Teatised",
"BIBLE_VERSION": "Piibli versioon",
"CHANGE_DISPLAY_MODE": "Muuda Kuvamisrežiimi",
"CHORD_VIEW": "Akordi Vaade",
"CHORDS": "Akorde",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Tagasi Kontrolleri Juurde",
"HIDE_NOTES": "Märkmete Peitmine",
"LOGIN": "Login",
"LOGIN_FAILED": "Sisselogimine ebaõnnestus",
"LOGIN_SUCCEEDED": "Edukalt sisse logitud",
"MAIN_VIEW": "Põhivaade",
"NEXT_ITEM": "Järgmine Päevakorrapunkt",
"NEXT_SLIDE": "Järgmine Slaid",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Teema Valikud",
"USER_NAME": "Kasutajanimi",
"USER_INTERFACE": "Kasutajaliides"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Lisää Tuote Palveluun",
"ADD_TO_SERVICE": "Lisää Palveluun",
"ALERT": "Huomio Viesti",
"ALERT_SUBMITTED": "Ilmoitus Lähetetty",
"ALERTS": "Huomio Viestit",
"BIBLE_VERSION": "Raamatun versio",
"CHANGE_DISPLAY_MODE": "Vaihda Näyttötilaa",
"CHORD_VIEW": "Sointunäkymä",
"CHORDS": "Soinnut",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Palaa Ohjaimeen",
"HIDE_NOTES": "Piilota Muistiinpanot",
"LOGIN": "Kirjaudu",
"LOGIN_FAILED": "Kirjautuminen epäonnistui",
"LOGIN_SUCCEEDED": "Kirjautuminen onnistui",
"MAIN_VIEW": "Päänäkymä",
"NEXT_ITEM": "Seuraava Kohta",
"NEXT_SLIDE": "Seuraava Dia",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Teeman Asetukset",
"USER_NAME": "Käyttäjänimi",
"USER_INTERFACE": "Käyttöliittymä"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Ajouter un Élément au Service",
"ADD_TO_SERVICE": "Ajouter au Service",
"ALERT": "Alerte",
"ALERT_SUBMITTED": "Alerte Soumise",
"ALERTS": "Alertes",
"BIBLE_VERSION": "Version de la Bible",
"CHANGE_DISPLAY_MODE": "Changer le Mode dAffichage",
"CHORD_VIEW": "Vue des Accords",
"CHORDS": "Accords",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Retour au Contrôleur",
"HIDE_NOTES": "Masquer les Notes",
"LOGIN": "Connexion",
"LOGIN_FAILED": "Echec de la connexion",
"LOGIN_SUCCEEDED": "Connexion réussie",
"MAIN_VIEW": "Vue Principale",
"NEXT_ITEM": "Élément Suivant",
"NEXT_SLIDE": "Diapositive Suivante",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Options de Thème",
"USER_NAME": "Nom dUtilisateur",
"USER_INTERFACE": "Interface Utilisateur"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Elem Hozzáadása a Szolgáltatáshoz",
"ADD_TO_SERVICE": "Hozzáadás a Szolgáltatáshoz",
"ALERT": "Riasztási",
"ALERT_SUBMITTED": "Riasztás Elküldve",
"ALERTS": "Riasztások",
"BIBLE_VERSION": "Bibliai változat",
"CHANGE_DISPLAY_MODE": "Megjelenítési mód Módosítása",
"CHORD_VIEW": "Akkord Nézet",
"CHORDS": "Akkordok",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Vissza a Kontrollerhez",
"HIDE_NOTES": "Jegyzetek Elrejtése",
"LOGIN": "Bejelentkezés",
"LOGIN_FAILED": "Sikertelen bejelentkezés",
"LOGIN_SUCCEEDED": "Sikeresen bejelentkezve",
"MAIN_VIEW": "Fő Nézet",
"NEXT_ITEM": "Következő Elem",
"NEXT_SLIDE": "Következő Dia",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Téma Beállítások",
"USER_NAME": "Felhasználónév",
"USER_INTERFACE": "Felhasználói Felület"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Tambahkan Item ke Layanan",
"ADD_TO_SERVICE": "Tambahkan ke Layanan",
"ALERT": "Peringatan",
"ALERT_SUBMITTED": "Peringatan Dikirim",
"ALERTS": "Peringatan",
"BIBLE_VERSION": "Versi Alkitab",
"CHANGE_DISPLAY_MODE": "Ubah Mode Tampilan",
"CHORD_VIEW": "Tampilan Kunci",
"CHORDS": "Kunci",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Kembali ke Pengontrol",
"HIDE_NOTES": "Sembunyikan Catatan",
"LOGIN": "Masuk",
"LOGIN_FAILED": "Login gagal",
"LOGIN_SUCCEEDED": "Berhasil login",
"MAIN_VIEW": "Tampilan Utama",
"NEXT_ITEM": "Item Berikutnya",
"NEXT_SLIDE": "Slide Berikutnya",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opsi Tema",
"USER_NAME": "Nama Pengguna",
"USER_INTERFACE": "Antarmuka Pengguna"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Aggiungi Elemento al Servizio",
"ADD_TO_SERVICE": "Aggiungi al Servizio",
"ALERT": "Allarme",
"ALERT_SUBMITTED": "Avviso Inviato",
"ALERTS": "Avvisi",
"BIBLE_VERSION": "Versione biblica",
"CHANGE_DISPLAY_MODE": "Cambia Modalità di Visualizzazione",
"CHORD_VIEW": "Vista degli Accordi",
"CHORDS": "Accordi",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Torna al Controller",
"HIDE_NOTES": "Nascondi le Note",
"LOGIN": "Accedi",
"LOGIN_FAILED": "Accesso non riuscito",
"LOGIN_SUCCEEDED": "Accesso effettuato con successo",
"MAIN_VIEW": "Vista Principale",
"NEXT_ITEM": "Prossimo Elemento",
"NEXT_SLIDE": "Prossima Diapositiva",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opzioni del Tema",
"USER_NAME": "Nome Utente",
"USER_INTERFACE": "Interfaccia Utente"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Aggiungi Elemento al Servizio",
"ADD_TO_SERVICE": "Aggiungi al Servizio",
"ALERT": "Allarme",
"ALERT_SUBMITTED": "Avviso Inviato",
"ALERTS": "Avvisi",
"BIBLE_VERSION": "Versione biblica",
"CHANGE_DISPLAY_MODE": "Cambia Modalità di Visualizzazione",
"CHORD_VIEW": "Vista degli Accordi",
"CHORDS": "Accordi",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Torna al Controller",
"HIDE_NOTES": "Nascondi le Note",
"LOGIN": "Accedi",
"LOGIN_FAILED": "Accesso non riuscito",
"LOGIN_SUCCEEDED": "Accesso effettuato con successo",
"MAIN_VIEW": "Vista Principale",
"NEXT_ITEM": "Prossimo Elemento",
"NEXT_SLIDE": "Prossima Diapositiva",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opzioni del Tema",
"USER_NAME": "Nome Utente",
"USER_INTERFACE": "Interfaccia Utente"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Aggiungi Elemento al Servizio",
"ADD_TO_SERVICE": "Aggiungi al Servizio",
"ALERT": "Allarme",
"ALERT_SUBMITTED": "Avviso Inviato",
"ALERTS": "Avvisi",
"BIBLE_VERSION": "Versione biblica",
"CHANGE_DISPLAY_MODE": "Cambia Modalità di Visualizzazione",
"CHORD_VIEW": "Vista degli Accordi",
"CHORDS": "Accordi",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Torna al Controller",
"HIDE_NOTES": "Nascondi le Note",
"LOGIN": "Accedi",
"LOGIN_FAILED": "Accesso non riuscito",
"LOGIN_SUCCEEDED": "Accesso effettuato con successo",
"MAIN_VIEW": "Vista Principale",
"NEXT_ITEM": "Prossimo Elemento",
"NEXT_SLIDE": "Prossima Diapositiva",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opzioni del Tema",
"USER_NAME": "Nome Utente",
"USER_INTERFACE": "Interfaccia Utente"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "サービスにアイテムを追加",
"ADD_TO_SERVICE": "サービスに追加",
"ALERT": "アラート",
"ALERT_SUBMITTED": "アラート送信済み",
"ALERTS": "アラート",
"BIBLE_VERSION": "聖書版",
"CHANGE_DISPLAY_MODE": "表示モードを変更",
"CHORD_VIEW": "コード表示",
"CHORDS": "コード",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "コントローラーに戻る",
"HIDE_NOTES": "ノートを非表示",
"LOGIN": "ログイン",
"LOGIN_FAILED": "ログインに失敗しました",
"LOGIN_SUCCEEDED": "ログインに成功しました",
"MAIN_VIEW": "メインビュー",
"NEXT_ITEM": "次のアイテム",
"NEXT_SLIDE": "次のスライド",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "テーマオプション",
"USER_NAME": "ユーザー名",
"USER_INTERFACE": "ユーザーインターフェース"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "서비스에 항목 추가",
"ADD_TO_SERVICE": "서비스에 추가",
"ALERT": "경고",
"ALERT_SUBMITTED": "경고가 제출됨",
"ALERTS": "경고",
"BIBLE_VERSION": "성경 번역본",
"CHANGE_DISPLAY_MODE": "디스플레이 모드 변경",
"CHORD_VIEW": "코드 보기",
"CHORDS": "코드",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "컨트롤러로 돌아가기",
"HIDE_NOTES": "메모 숨기기",
"LOGIN": "로그인",
"LOGIN_FAILED": "로그인 실패",
"LOGIN_SUCCEEDED": "성공적으로 로그인했습니다.",
"MAIN_VIEW": "주 화면",
"NEXT_ITEM": "다음 항목",
"NEXT_SLIDE": "다음 슬라이드",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "테마 옵션",
"USER_NAME": "사용자 이름",
"USER_INTERFACE": "사용자 인터페이스"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Pridėti Elementą prie Paslaugos",
"ADD_TO_SERVICE": "Pridėti prie Paslaugos",
"ALERT": "Įspėjimas",
"ALERT_SUBMITTED": "Pateiktas Įspėjimas",
"ALERTS": "Įspėjimai",
"BIBLE_VERSION": "Biblijos versija",
"CHANGE_DISPLAY_MODE": "Keisti Rodymo Režimą",
"CHORD_VIEW": "Akordei",
"CHORDS": "Akordei",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Grįžti į Valdiklį",
"HIDE_NOTES": "Slėpti Pastabas",
"LOGIN": "Prisijungti",
"LOGIN_FAILED": "Prisijungti nepavyko",
"LOGIN_SUCCEEDED": "Sėkmingai prisijungęs",
"MAIN_VIEW": "Pagrindinis Rodinys",
"NEXT_ITEM": "Kitas Elementas",
"NEXT_SLIDE": "Kitas Skaidrė",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Temos Parinktys",
"USER_NAME": "Vartotojo Vardas",
"USER_INTERFACE": "Vartotojo Sąsaja"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Legg til Element i Tjenesten",
"ADD_TO_SERVICE": "Legg til i Tjenesten",
"ALERT": "Varsel",
"ALERT_SUBMITTED": "Varsel Sendt Inn",
"ALERTS": "Varsler",
"BIBLE_VERSION": "Bibelens versjon",
"CHANGE_DISPLAY_MODE": "Endre Visningsmodus",
"CHORD_VIEW": "Akkordvisning",
"CHORDS": "Akkorder",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Gå Tilbake til Kontrolleren",
"HIDE_NOTES": "Skjul Notater",
"LOGIN": "Logg Inn",
"LOGIN_FAILED": "Innlogging mislyktes",
"LOGIN_SUCCEEDED": "Vellykket innlogging",
"MAIN_VIEW": "Hovedvisning",
"NEXT_ITEM": "Neste Element",
"NEXT_SLIDE": "Neste Lysbilde",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Temaopsjoner",
"USER_NAME": "Brukernavn",
"USER_INTERFACE": "Brukergrensesnitt"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Voeg Item Toe aan Liturgie",
"ADD_TO_SERVICE": "Voeg Toe aan Liturgie",
"ALERT": "Waarschuwing",
"ALERT_SUBMITTED": "Waarschuwing Verstuurd",
"ALERTS": "Waarschuwingen",
"BIBLE_VERSION": "Bijbelversie",
"CHANGE_DISPLAY_MODE": "Wijzig Weergave",
"CHORD_VIEW": "Akkoordenscherm",
"CHORDS": "Akkoorden",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Ga Terug naar Controller",
"HIDE_NOTES": "Verberg Notities",
"LOGIN": "Log In",
"LOGIN_FAILED": "Log in mislukt",
"LOGIN_SUCCEEDED": "Succesvol ingelogd",
"MAIN_VIEW": "Hoofdscherm",
"NEXT_ITEM": "Volgend Item",
"NEXT_SLIDE": "Volgende Slide",
@ -46,7 +50,7 @@
"STAGE_VIEW": "Podiumscherm",
"THEMES": "Thema's",
"THEME_LEVEL": "Thema Niveau",
"THEME_OPTIONS": "Theme Opties",
"THEME_OPTIONS": "Thema Opties",
"USER_NAME": "Gebruikersnaam",
"USER_INTERFACE": "Gebruikersinterface"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Dodaj Element do Usługi",
"ADD_TO_SERVICE": "Dodaj do Usługi",
"ALERT": "Alert",
"ALERT_SUBMITTED": "Przesłano Alert",
"ALERTS": "Alerty",
"BIBLE_VERSION": "Wersja Biblii",
"CHANGE_DISPLAY_MODE": "Zmień Tryb Wyświetlania",
"CHORD_VIEW": "Widok Akordów",
"CHORDS": "Akordy",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Wróć do Kontrolera",
"HIDE_NOTES": "Ukryj Notatki",
"LOGIN": "Zaloguj Się",
"LOGIN_FAILED": "Logowanie nie powiodło się",
"LOGIN_SUCCEEDED": "Pomyślnie zalogowano",
"MAIN_VIEW": "Widok Główny",
"NEXT_ITEM": "Następny Element",
"NEXT_SLIDE": "Następny Slajd",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opcje Motywu",
"USER_NAME": "Nazwa Użytkownika",
"USER_INTERFACE": "Interfejs Użytkownika"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Adicionar Item ao Serviço",
"ADD_TO_SERVICE": "Adicionar ao Serviço",
"ALERT": "Alerta",
"ALERT_SUBMITTED": "Alerta Enviado",
"ALERTS": "Alertas",
"BIBLE_VERSION": "Versão da Bíblia",
"CHANGE_DISPLAY_MODE": "Alterar Modo de Exibição",
"CHORD_VIEW": "Visualização de Acordes",
"CHORDS": "Acordes",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Voltar para o Controlador",
"HIDE_NOTES": "Ocultar Notas",
"LOGIN": "Login",
"LOGIN_FAILED": "Falha no login",
"LOGIN_SUCCEEDED": "Efetuado login com êxito",
"MAIN_VIEW": "Visualização Principal",
"NEXT_ITEM": "Próximo Item",
"NEXT_SLIDE": "Próximo Slide",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opções de Tema",
"USER_NAME": "Nome de Usuário",
"USER_INTERFACE": "Interface do Usuário"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Adăugați Element la Serviciu",
"ADD_TO_SERVICE": "Adăugați la Serviciu",
"ALERT": "Alertă",
"ALERT_SUBMITTED": "Alertă Transmisă",
"ALERTS": "Alerte",
"BIBLE_VERSION": "Versiunea Bibliei",
"CHANGE_DISPLAY_MODE": "Schimbați Modul de Afișare",
"CHORD_VIEW": "Vizualizare Acorduri",
"CHORDS": "Acorduri",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Înapoi la Controler",
"HIDE_NOTES": "Ascundeți Notele",
"LOGIN": "Conectare",
"LOGIN_FAILED": "Conectarea nu a reușit",
"LOGIN_SUCCEEDED": "Conectat cu succes",
"MAIN_VIEW": "Vizualizare Principală",
"NEXT_ITEM": "Elementul Următo",
"NEXT_SLIDE": "Diapozitivul Următo",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Opțiuni Temă",
"USER_NAME": "Nume Utilizator",
"USER_INTERFACE": "Interfață Utilizator"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Добавить элемент в службу",
"ADD_TO_SERVICE": "Добавить в службу",
"ALERT": "Оповещение",
"ALERT_SUBMITTED": "Оповещение отправлено",
"ALERTS": "Оповещения",
"BIBLE_VERSION": "Перевод Библии",
"CHANGE_DISPLAY_MODE": "Изменить режим отображения",
"CHORD_VIEW": "Просмотр аккордов",
"CHORDS": "Аккорды",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Вернуться к контроллеру",
"HIDE_NOTES": "Скрыть заметки",
"LOGIN": "Войти",
"LOGIN_FAILED": "Не удалось войти в систему",
"LOGIN_SUCCEEDED": "Успешно вошел в систему",
"MAIN_VIEW": "Основное представление",
"NEXT_ITEM": "Следующий элемент",
"NEXT_SLIDE": "Следующий слайд",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Варианты темы",
"USER_NAME": "Имя пользователя",
"USER_INTERFACE": "Пользовательский интерфейс"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Pridať Položku do Služby",
"ADD_TO_SERVICE": "Pridať do Služby",
"ALERT": "Upozornenie",
"ALERT_SUBMITTED": "Upozornenie Bolo Odoslané",
"ALERTS": "Upozornenia",
"BIBLE_VERSION": "Verzia Biblie",
"CHANGE_DISPLAY_MODE": "Zmeniť Režim Zobrazenia",
"CHORD_VIEW": "Zobrazenie Akordov",
"CHORDS": "Akordy",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Ísť Späť na Ovládač",
"HIDE_NOTES": "Skryť Poznámky",
"LOGIN": "Prihlásenie",
"LOGIN_FAILED": "Prihlásenie zlyhalo",
"LOGIN_SUCCEEDED": "Úspešne prihlásené",
"MAIN_VIEW": "Hlavný Pohľad",
"NEXT_ITEM": "Ďalšia Položka",
"NEXT_SLIDE": "Ďalší Snímok",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Možnosti Témy",
"USER_NAME": "Meno Používateľa",
"USER_INTERFACE": "Používateľské Rozhranie"
}
}

View File

@ -1,52 +1,56 @@
{
"ADD_ITEM": "Dodaj Element",
"ADD_ITEM_TO_SERVICE": "Dodaj Element Spored",
"ADD_TO_SERVICE": "Dodaj Spored",
"ADD_ITEM": "Dodaj Predmet",
"ADD_ITEM_TO_SERVICE": "Dodaj Predmet v Spored",
"ADD_TO_SERVICE": "Dodaj v Spored",
"ALERT": "Opozorilo",
"ALERT_SUBMITTED": "Predloženo Opozorilo",
"ALERTS": "Opozorila",
"CHANGE_DISPLAY_MODE": "Spremeni način Prikaza",
"CHORD_VIEW": "Pogled Akordov",
"CHORDS": "Akordi",
"BIBLE_VERSION": "Svetopisemska različica",
"CHANGE_DISPLAY_MODE": "Spremeni Način Prikaza",
"CHORD_VIEW": "Pogled s Sozvoki",
"CHORDS": "Sozvoki",
"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",
"DISCONNECTED": "Nepovezan",
"ENABLE_BIG_DISPLAY_BUTTONS": "Omogoči Velike Gumbe",
"ENABLE_FAST_SWITCHING_PANEL": "Omogoči Priročno Vrstico",
"FONT_SCALE": "Velikost Pisave",
"GLOBAL": "Globalno",
"GO_BACK_TO_CONTROLLER": "Vrni se na Krmilnikr",
"GLOBAL": "Obče",
"GO_BACK_TO_CONTROLLER": "Vrni se na Upravljalnik",
"HIDE_NOTES": "Skrij Opombe",
"LOGIN": "Prijava",
"LOGIN_FAILED": "Prijava ni uspela",
"LOGIN_SUCCEEDED": "Uspešno prijavljeni",
"MAIN_VIEW": "Glavni Pogled",
"NEXT_ITEM": "Naslednji Element",
"NEXT_SLIDE": "Naslednji Diapozitiv",
"NEXT_ITEM": "Naslednji Predmet",
"NEXT_SLIDE": "Naslednja Prosojnica",
"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",
"NO_SERVICE_ITEMS": "Ni Predmetov Sporeda",
"NO_SLIDE_ITEMS": "Ni Prosojnic",
"PASSWORD": "Geslo",
"PREVIOUS_ITEM": "Prejšnji Element",
"PREVIOUS_SLIDE": "Prejšnji Diapozitiv",
"PREVIOUS_ITEM": "Prejšnji Predmet",
"PREVIOUS_SLIDE": "Prejšnja Prosojnica",
"SEARCH": "Iskanje",
"SEARCH_RESULTS": "Rezultati Iskanja",
"SEARCH_TEXT": "Besedilo Iskanja",
"SEARCH_RESULTS": "Zadetki Iskanja",
"SEARCH_TEXT": "Iskano Besedilo",
"SEND": "Pošlji",
"SEND_AN_ALERT": "Pošlji Opozorilo",
"SEND_AN_ALERT": "Objavi 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_NOTES": "Prikaži Opombe",
"SHOW_PRESENTATION": "Prikaži Predstavitve",
"SLIDES": "Diapozitivi",
"SLIDES": "Prosojnice",
"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",
"SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED": "Spreminjanje teme na ravni pesmi ni podprto. Spremenite raven teme na Obče ali Spored",
"STAGE": "Oder",
"STAGE_AND_CHORDS_APPEARANCE": "Videz Odrskega Pogleda in Sozvokov",
"STAGE_VIEW": "Odrski Pogled",
"THEMES": "Teme",
"THEME_LEVEL": "Raven Teme",
"THEME_OPTIONS": "Možnosti Teme",
"USER_NAME": "Uporabniško Ime",
"USER_INTERFACE": "Uporabniški Vmesnik"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Lägg till Objekt i Tjänsten",
"ADD_TO_SERVICE": "Lägg till i Tjänsten",
"ALERT": "Varning",
"ALERT_SUBMITTED": "Avisering Skickad",
"ALERTS": "Varningar",
"BIBLE_VERSION": "Bibelöversättning",
"CHANGE_DISPLAY_MODE": "Ändra Visningsläge",
"CHORD_VIEW": "Ackordvy",
"CHORDS": "Ackord",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Gå Tillbaka till Kontrollenhet",
"HIDE_NOTES": "Dölj Anteckningar",
"LOGIN": "Logga In",
"LOGIN_FAILED": "Inloggningen misslyckades",
"LOGIN_SUCCEEDED": "Har loggat in",
"MAIN_VIEW": "Huvudvy",
"NEXT_ITEM": "Nästa Objekt",
"NEXT_SLIDE": "Nästa Bild",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Temaval",
"USER_NAME": "Användarnamn",
"USER_INTERFACE": "Användargränssnitt"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "பொருள் சேர்க்க சேவைக்கு",
"ADD_TO_SERVICE": "சேவைக்கு சேர்க்கவும்",
"ALERT": "எச்சரிக்கை",
"ALERT_SUBMITTED": "எச்சரிக்கை சமர்ப்பிக்கப்பட்டது",
"ALERTS": "எச்சரிக்கைகள்",
"BIBLE_VERSION": "பைபிள் பதிப்பு",
"CHANGE_DISPLAY_MODE": "காட்சி முறையை மாற்று",
"CHORD_VIEW": "கார்டு காட்சி",
"CHORDS": "கார்டுகள்",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "கட்டுப்பாட்டிற்கு திரும்பு",
"HIDE_NOTES": "குறிப்புகளை மறை",
"LOGIN": "உள்நுழை",
"LOGIN_FAILED": "உள்நுழைவு தோல்வியுற்றது",
"LOGIN_SUCCEEDED": "வெற்றிகரமாக உள்நுழைந்தது",
"MAIN_VIEW": "முக்கிய காட்சி",
"NEXT_ITEM": "அடுத்த பொருள்",
"NEXT_SLIDE": "அடுத்த ஸ்லைட்",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "தீம் விருப்பங்கள்",
"USER_NAME": "பயனர் பெயர்",
"USER_INTERFACE": "பயனர் இடைமுகம்"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "เพิ่มรายการในบริการ",
"ADD_TO_SERVICE": "เพิ่มในบริการ",
"ALERT": "แจ้งเตือน",
"ALERT_SUBMITTED": "ส่งการแจ้งเตือนแล้ว",
"ALERTS": "การแจ้งเตือน",
"BIBLE_VERSION": "ฉบับพระคัมภีร์",
"CHANGE_DISPLAY_MODE": "เปลี่ยนโหมดการแสดงผล",
"CHORD_VIEW": "มุมมองคอร์ด",
"CHORDS": "คอร์ด",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "กลับไปที่ควบคุม",
"HIDE_NOTES": "ซ่อนบันทึก",
"LOGIN": "เข้าสู่ระบบ",
"LOGIN_FAILED": "การเข้าสู่ระบบล้มเหลว",
"LOGIN_SUCCEEDED": "เข้าสู่ระบบสําเร็จ",
"MAIN_VIEW": "มุมมองหลัก",
"NEXT_ITEM": " รายการถัดไป",
"NEXT_SLIDE": "ภาพนิ่งถัดไป",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "ตัวเลือกธีม",
"USER_NAME": "ชื่อผู้ใช้",
"USER_INTERFACE": "ส่วนติดต่อผู้ใช้"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "Thêm mục vào Dịch vụ",
"ADD_TO_SERVICE": "Thêm vào Dịch vụ",
"ALERT": "Cảnh báo",
"ALERT_SUBMITTED": "Cảnh báo đã Gửi",
"ALERTS": "Các cảnh báo",
"BIBLE_VERSION": "Phiên bản Kinh Thánh",
"CHANGE_DISPLAY_MODE": "Thay đổi Chế độ Hiển thị",
"CHORD_VIEW": "Xem hợp âm",
"CHORDS": "Hợp âm",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "Quay lại Bộ điều khiển",
"HIDE_NOTES": "Ẩn ghi chú",
"LOGIN": "Đăng nhập",
"LOGIN_FAILED": "Đăng nhập không thành công",
"LOGIN_SUCCEEDED": "Đăng nhập thành công",
"MAIN_VIEW": "Chế độ xem chính",
"NEXT_ITEM": "Mục tiếp theo",
"NEXT_SLIDE": "Trang tiếp theo",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "Tùy chọn chủ đề",
"USER_NAME": "Tên người dùn",
"USER_INTERFACE": "Giao diện người dùng"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "添加项目到服务",
"ADD_TO_SERVICE": "添加到服务",
"ALERT": "警报",
"ALERT_SUBMITTED": "警报已提交",
"ALERTS": "警报",
"BIBLE_VERSION": "圣经版本",
"CHANGE_DISPLAY_MODE": "更改显示模式",
"CHORD_VIEW": "和弦视图",
"CHORDS": "和弦",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "返回到控制器",
"HIDE_NOTES": "隐藏注释",
"LOGIN": "登录",
"LOGIN_FAILED": "登录失败",
"LOGIN_SUCCEEDED": "登录成功",
"MAIN_VIEW": "主视图",
"NEXT_ITEM": "下一个项目",
"NEXT_SLIDE": "下一张幻灯片",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "主题选项",
"USER_NAME": "用户名",
"USER_INTERFACE": "用户界面"
}
}

View File

@ -3,7 +3,9 @@
"ADD_ITEM_TO_SERVICE": "新增項目至服務",
"ADD_TO_SERVICE": "新增至服務",
"ALERT": "警示",
"ALERT_SUBMITTED": "警報已提交",
"ALERTS": "警示訊息",
"BIBLE_VERSION": "聖經版本",
"CHANGE_DISPLAY_MODE": "變更顯示模式",
"CHORD_VIEW": "和弦檢視",
"CHORDS": "和弦",
@ -16,6 +18,8 @@
"GO_BACK_TO_CONTROLLER": "返回控制器",
"HIDE_NOTES": "隱藏筆記",
"LOGIN": "登入",
"LOGIN_FAILED": "登錄失敗",
"LOGIN_SUCCEEDED": "登錄成功",
"MAIN_VIEW": "主畫面",
"NEXT_ITEM": "下一項目",
"NEXT_SLIDE": "下一張投影片",
@ -49,4 +53,4 @@
"THEME_OPTIONS": "主題選項",
"USER_NAME": "使用者名稱",
"USER_INTERFACE": "使用者介面"
}
}

View File

@ -3,7 +3,6 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import 'hammerjs';
if (environment.production) {
enableProdMode();

3643
yarn.lock

File diff suppressed because it is too large Load Diff