Force capitalization in translations.

This commit is contained in:
Chris Witterholt 2024-05-07 05:46:40 +00:00
parent bf185b0f92
commit 1144564c2c
24 changed files with 642 additions and 582 deletions

View File

@ -25,23 +25,23 @@
"tx": "node scripts/tx.js" "tx": "node scripts/tx.js"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "^17.3.6", "@angular/animations": "^17.3.7",
"@angular/cdk": "^17.3.6", "@angular/cdk": "^17.3.7",
"@angular/common": "^17.3.6", "@angular/common": "^17.3.7",
"@angular/compiler": "^17.3.6", "@angular/compiler": "^17.3.7",
"@angular/core": "^17.3.6", "@angular/core": "^17.3.7",
"@angular/forms": "^17.3.6", "@angular/forms": "^17.3.7",
"@angular/material": "^17.3.6", "@angular/material": "^17.3.7",
"@angular/platform-browser": "^17.3.6", "@angular/platform-browser": "^17.3.7",
"@angular/platform-browser-dynamic": "^17.3.6", "@angular/platform-browser-dynamic": "^17.3.7",
"@angular/router": "^17.3.6", "@angular/router": "^17.3.7",
"@fontsource/roboto": "^5.0.13", "@fontsource/roboto": "^5.0.13",
"@ngx-translate/core": "^15.0.0", "@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0", "@ngx-translate/http-loader": "^8.0.0",
"core-js": "^3.37.0", "core-js": "^3.37.0",
"material-icons": "^1.13.12", "material-icons": "^1.13.12",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"zone.js": "^0.14.4" "zone.js": "^0.14.5"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^17.3.6", "@angular-devkit/build-angular": "^17.3.6",
@ -51,16 +51,16 @@
"@angular-eslint/schematics": "^17.3.0", "@angular-eslint/schematics": "^17.3.0",
"@angular-eslint/template-parser": "^17.3.0", "@angular-eslint/template-parser": "^17.3.0",
"@angular/cli": "~17.3.6", "@angular/cli": "~17.3.6",
"@angular/compiler-cli": "^17.3.6", "@angular/compiler-cli": "^17.3.7",
"@angular/language-service": "^17.3.6", "@angular/language-service": "^17.3.7",
"@chiragrupani/karma-chromium-edge-launcher": "^2.3.1", "@chiragrupani/karma-chromium-edge-launcher": "^2.3.1",
"@transifex/api": "^7.1.0", "@transifex/api": "^7.1.0",
"@types/jasmine": "~5.1.4", "@types/jasmine": "~5.1.4",
"@types/jasminewd2": "~2.0.13", "@types/jasminewd2": "~2.0.13",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/node": "~20.12.7", "@types/node": "~20.12.8",
"@typescript-eslint/eslint-plugin": "7.7.1", "@typescript-eslint/eslint-plugin": "7.8.0",
"@typescript-eslint/parser": "7.7.1", "@typescript-eslint/parser": "7.8.0",
"axios": "^1.6.8", "axios": "^1.6.8",
"browserslist": "^4.23.0", "browserslist": "^4.23.0",
"browserslist-useragent-regexp": "^4.1.3", "browserslist-useragent-regexp": "^4.1.3",

View File

@ -3,13 +3,13 @@
<button mat-icon-button (click)="menu.toggle()"> <button mat-icon-button (click)="menu.toggle()">
<mat-icon>menu</mat-icon> <mat-icon>menu</mat-icon>
</button> </button>
<span class="page-title">{{ pageTitle | translate }}</span> <span class="page-title">{{ pageTitle | translate | titlecase }}</span>
<span class="spacer"></span> <span class="spacer"></span>
@if (showLogin) { @if (showLogin) {
<button <button
mat-button mat-button
(click)="login()"> (click)="login()">
{{ 'LOGIN' | translate }} {{ 'LOGIN' | translate | titlecase }}
</button> </button>
} }
@if (webSocketOpen) { @if (webSocketOpen) {
@ -17,7 +17,7 @@
mat-icon-button mat-icon-button
(click)="forceWebSocketReconnection()" (click)="forceWebSocketReconnection()"
class="connection-status" class="connection-status"
matTooltip="{{ 'CONNECTED_TO_OPENLP' | translate }}"> matTooltip="{{ 'CONNECTED_TO_OPENLP' | translate | titlecase }}">
<mat-icon>link</mat-icon> <mat-icon>link</mat-icon>
</button> </button>
} }
@ -26,7 +26,7 @@
mat-icon-button mat-icon-button
(click)="forceWebSocketReconnection()" (click)="forceWebSocketReconnection()"
class="connection-status" class="connection-status"
matTooltip="{{ 'DISCONNECTED' | translate }}"> matTooltip="{{ 'DISCONNECTED' | translate | titlecase }}">
<mat-icon>link_off</mat-icon> <mat-icon>link_off</mat-icon>
</button> </button>
} }
@ -41,58 +41,58 @@
routerLink="/service" routerLink="/service"
routerLinkActive #serviceRoute="routerLinkActive" routerLinkActive #serviceRoute="routerLinkActive"
[activated]="serviceRoute.isActive"> [activated]="serviceRoute.isActive">
<mat-icon>list</mat-icon> {{ 'SERVICE' | translate }} <mat-icon>list</mat-icon> {{ 'SERVICE' | translate | titlecase }}
</a> </a>
<a mat-list-item <a mat-list-item
(click)="menu.close()" (click)="menu.close()"
routerLink="/slides" routerLink="/slides"
routerLinkActive #slidesRoute="routerLinkActive" routerLinkActive #slidesRoute="routerLinkActive"
[activated]="slidesRoute.isActive"> [activated]="slidesRoute.isActive">
<mat-icon>collections</mat-icon> {{ 'SLIDES' | translate }} <mat-icon>collections</mat-icon> {{ 'SLIDES' | translate | titlecase }}
</a> </a>
<a mat-list-item <a mat-list-item
(click)="menu.close()" (click)="menu.close()"
routerLink="/alerts" routerLink="/alerts"
routerLinkActive #alertsRoute="routerLinkActive" routerLinkActive #alertsRoute="routerLinkActive"
[activated]="alertsRoute.isActive"> [activated]="alertsRoute.isActive">
<mat-icon>error</mat-icon> {{ 'ALERTS' | translate }} <mat-icon>error</mat-icon> {{ 'ALERTS' | translate | titlecase }}
</a> </a>
<a mat-list-item <a mat-list-item
(click)="menu.close()" (click)="menu.close()"
routerLink="/search" routerLink="/search"
routerLinkActive #searchRoute="routerLinkActive" routerLinkActive #searchRoute="routerLinkActive"
[activated]="searchRoute.isActive"> [activated]="searchRoute.isActive">
<mat-icon>search</mat-icon> {{ 'SEARCH' | translate }} <mat-icon>search</mat-icon> {{ 'SEARCH' | translate | titlecase }}
</a> </a>
<a mat-list-item <a mat-list-item
(click)="menu.close()" (click)="menu.close()"
routerLink="/themes" routerLink="/themes"
routerLinkActive #themesRoute="routerLinkActive" routerLinkActive #themesRoute="routerLinkActive"
[activated]="themesRoute.isActive"> [activated]="themesRoute.isActive">
<mat-icon>image</mat-icon> {{ 'THEMES' | translate }} <mat-icon>image</mat-icon> {{ 'THEMES' | translate | titlecase }}
</a> </a>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<a mat-list-item <a mat-list-item
(click)="menu.close()" (click)="menu.close()"
routerLink="/main"> routerLink="/main">
{{ 'MAIN_VIEW' | translate }} {{ 'MAIN_VIEW' | translate | titlecase }}
</a> </a>
<a mat-list-item <a mat-list-item
(click)="menu.close()" (click)="menu.close()"
routerLink="/stage"> routerLink="/stage">
{{ 'STAGE_VIEW' | translate }} {{ 'STAGE_VIEW' | translate | titlecase }}
</a> </a>
<a mat-list-item <a mat-list-item
(click)="menu.close()" (click)="menu.close()"
routerLink="/chords"> routerLink="/chords">
{{ 'CHORD_VIEW' | translate }} {{ 'CHORD_VIEW' | translate | titlecase }}
</a> </a>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<a mat-list-item (click)="menu.close()" <a mat-list-item (click)="menu.close()"
routerLink="/settings" routerLink="/settings"
routerLinkActive #settingsRoute="routerLinkActive" routerLinkActive #settingsRoute="routerLinkActive"
[activated]="settingsRoute.isActive"> [activated]="settingsRoute.isActive">
<mat-icon>settings</mat-icon> {{ 'SETTINGS' | translate }} <mat-icon>settings</mat-icon> {{ 'SETTINGS' | translate | titlecase }}
</a> </a>
</mat-nav-list> </mat-nav-list>
</mat-sidenav> </mat-sidenav>
@ -113,28 +113,28 @@
<button <button
mat-fab color="primary" mat-fab color="primary"
(click)="previousItem()" (click)="previousItem()"
matTooltip="{{ 'PREVIOUS_ITEM' | translate }}" matTooltip="{{ 'PREVIOUS_ITEM' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>first_page</mat-icon> <mat-icon>first_page</mat-icon>
</button> </button>
<button <button
mat-fab color="primary" mat-fab color="primary"
(click)="nextItem()" (click)="nextItem()"
matTooltip="{{ 'NEXT_ITEM' | translate }}" matTooltip="{{ 'NEXT_ITEM' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>last_page</mat-icon> <mat-icon>last_page</mat-icon>
</button> </button>
<button <button
mat-fab color="primary" mat-fab color="primary"
(click)="previousSlide()" (click)="previousSlide()"
matTooltip="{{ 'PREVIOUS_SLIDE' | translate }}" matTooltip="{{ 'PREVIOUS_SLIDE' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>navigate_before</mat-icon> <mat-icon>navigate_before</mat-icon>
</button> </button>
<button <button
mat-fab color="primary" mat-fab color="primary"
(click)="nextSlide()" (click)="nextSlide()"
matTooltip="{{ 'NEXT_SLIDE' | translate }}" matTooltip="{{ 'NEXT_SLIDE' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>navigate_next</mat-icon> <mat-icon>navigate_next</mat-icon>
</button> </button>
@ -143,7 +143,7 @@
#squashedDisplayButton #squashedDisplayButton
(click)="openDisplayModeSelector()" (click)="openDisplayModeSelector()"
class="squashed-display-button" class="squashed-display-button"
matTooltip="{{ 'CHANGE_DISPLAY_MODE' | translate }}" matTooltip="{{ 'CHANGE_DISPLAY_MODE' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
@if (state.blank) { @if (state.blank) {
<mat-icon>videocam_off</mat-icon> <mat-icon>videocam_off</mat-icon>
@ -164,7 +164,7 @@
class="displayButton" class="displayButton"
[class.active]="state.blank" [class.active]="state.blank"
[disabled]="state.blank" [disabled]="state.blank"
matTooltip="{{ 'SHOW_BLACK' | translate }}" matTooltip="{{ 'SHOW_BLACK' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>videocam_off</mat-icon> <mat-icon>videocam_off</mat-icon>
</button> </button>
@ -174,7 +174,7 @@
class="displayButton" class="displayButton"
[class.active]="state.theme" [class.active]="state.theme"
[disabled]="state.theme" [disabled]="state.theme"
matTooltip="{{ 'SHOW_BACKGROUND' | translate }}" matTooltip="{{ 'SHOW_BACKGROUND' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>wallpaper</mat-icon> <mat-icon>wallpaper</mat-icon>
</button> </button>
@ -184,7 +184,7 @@
class="displayButton" class="displayButton"
[class.active]="state.display" [class.active]="state.display"
[disabled]="state.display" [disabled]="state.display"
matTooltip="{{ 'SHOW_DESKTOP' | translate }}" matTooltip="{{ 'SHOW_DESKTOP' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>desktop_windows</mat-icon> <mat-icon>desktop_windows</mat-icon>
</button> </button>
@ -194,7 +194,7 @@
class="displayButton" class="displayButton"
[class.active]="state.display" [class.active]="state.display"
[disabled]="state.live()" [disabled]="state.live()"
matTooltip="{{ 'SHOW_PRESENTATION' | translate }}" matTooltip="{{ 'SHOW_PRESENTATION' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>videocam</mat-icon> <mat-icon>videocam</mat-icon>
</button> </button>
@ -203,26 +203,26 @@
<button <button
mat-icon-button mat-icon-button
(click)="previousItem()" (click)="previousItem()"
matTooltip="{{ 'PREVIOUS_ITEM' | translate }}" matTooltip="{{ 'PREVIOUS_ITEM' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>first_page</mat-icon> <mat-icon>first_page</mat-icon>
</button> </button>
<button <button
mat-icon-button mat-icon-button
(click)="nextItem()" (click)="nextItem()"
matTooltip="{{ 'NEXT_ITEM' | translate }}" matTooltip="{{ 'NEXT_ITEM' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>last_page</mat-icon> <mat-icon>last_page</mat-icon>
</button> </button>
<button <button
mat-icon-button (click)="previousSlide()" mat-icon-button (click)="previousSlide()"
matTooltip="{{ 'PREVIOUS_SLIDE' | translate }}" matTooltip="{{ 'PREVIOUS_SLIDE' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>navigate_before</mat-icon> <mat-icon>navigate_before</mat-icon>
</button> </button>
<button <button
mat-icon-button (click)="nextSlide()" mat-icon-button (click)="nextSlide()"
matTooltip="{{ 'NEXT_SLIDE' | translate }}" matTooltip="{{ 'NEXT_SLIDE' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>navigate_next</mat-icon> <mat-icon>navigate_next</mat-icon>
</button> </button>
@ -231,7 +231,7 @@
#squashedDisplayButton #squashedDisplayButton
(click)="openDisplayModeSelector()" (click)="openDisplayModeSelector()"
class="squashed-display-button" class="squashed-display-button"
matTooltip="{{ 'CHANGE_DISPLAY_MODE' | translate }}" matTooltip="{{ 'CHANGE_DISPLAY_MODE' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
@if (state.blank) { @if (state.blank) {
<mat-icon>videocam_off</mat-icon> <mat-icon>videocam_off</mat-icon>
@ -251,7 +251,7 @@
class="displayButton" class="displayButton"
[class.active]="state.blank" [class.active]="state.blank"
[disabled]="state.blank" [disabled]="state.blank"
matTooltip="{{ 'SHOW_BLACK' | translate }}" matTooltip="{{ 'SHOW_BLACK' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>videocam_off</mat-icon> <mat-icon>videocam_off</mat-icon>
</button> </button>
@ -261,7 +261,7 @@
class="displayButton" class="displayButton"
[class.active]="state.theme" [class.active]="state.theme"
[disabled]="state.theme" [disabled]="state.theme"
matTooltip="{{ 'SHOW_BACKGROUND' | translate }}" matTooltip="{{ 'SHOW_BACKGROUND' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>wallpaper</mat-icon> <mat-icon>wallpaper</mat-icon>
</button> </button>
@ -270,7 +270,7 @@
class="displayButton" class="displayButton"
[class.active]="state.display" [class.active]="state.display"
[disabled]="state.display" [disabled]="state.display"
matTooltip="{{ 'SHOW_DESKTOP' | translate }}" matTooltip="{{ 'SHOW_DESKTOP' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>desktop_windows</mat-icon> <mat-icon>desktop_windows</mat-icon>
</button> </button>
@ -279,7 +279,7 @@
class="displayButton" class="displayButton"
[class.active]="state.display" [class.active]="state.display"
[disabled]="state.live()" [disabled]="state.live()"
matTooltip="{{ 'SHOW_PRESENTATION' | translate }}" matTooltip="{{ 'SHOW_PRESENTATION' | translate | titlecase }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>videocam</mat-icon> <mat-icon>videocam</mat-icon>
</button> </button>

View File

@ -13,7 +13,7 @@ import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators'; import { debounceTime } from 'rxjs/operators';
import { DisplayModeSelectorComponent } from './components/display-mode-selector/display-mode-selector.component'; import { DisplayModeSelectorComponent } from './components/display-mode-selector/display-mode-selector.component';
import { Shortcuts, ShortcutsService } from './shortcuts.service'; 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 { SettingsService } from './settings.service';
import * as supportedBrowsers from '../assets/supportedBrowsers'; import * as supportedBrowsers from '../assets/supportedBrowsers';

View File

@ -3,6 +3,7 @@ import { BrowserModule, Title } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { TitleCasePipe } from '@angular/common';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatButtonToggleModule } from '@angular/material/button-toggle';
@ -38,9 +39,9 @@ import { SearchOptionsComponent } from './components/search/search-options/searc
import { SlidesComponent } from './components/slides/slides.component'; import { SlidesComponent } from './components/slides/slides.component';
import { ChordViewComponent } from './components/chord-view/chord-view.component'; import { ChordViewComponent } from './components/chord-view/chord-view.component';
import { StageViewComponent } from './components/stage-view/stage-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 { 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 { LoginComponent } from './components/login/login.component';
import { ThemesComponent } from './components/themes/themes.component'; import { ThemesComponent } from './components/themes/themes.component';
import { SlideListComponent } from './components/slides/slide-list/slide-list.component'; import { SlideListComponent } from './components/slides/slide-list/slide-list.component';
@ -50,6 +51,7 @@ import { ServiceListComponent } from './components/service/service-list/service-
import { ChordViewItemComponent } from './components/chord-view/chord-view-item/chord-view-item.component'; 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 { StageViewItemComponent } from './components/stage-view/stage-view-item/stage-view-item.component';
import { DisplayModeSelectorComponent } from './components/display-mode-selector/display-mode-selector.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 { SettingsComponent } from './components/settings/settings.component';
import { StageChordPreviewComponent } from './components/settings/stage-chord-preview/stage-chord-preview.component'; import { StageChordPreviewComponent } from './components/settings/stage-chord-preview/stage-chord-preview.component';
@ -70,6 +72,7 @@ import { StageChordPreviewComponent } from './components/settings/stage-chord-pr
AlertComponent, AlertComponent,
SearchComponent, SearchComponent,
SearchOptionsComponent, SearchOptionsComponent,
SentenceCasePipe,
SlidesComponent, SlidesComponent,
SlideListComponent, SlideListComponent,
SlideItemComponent, SlideItemComponent,
@ -113,7 +116,9 @@ import { StageChordPreviewComponent } from './components/settings/stage-chord-pr
PageTitleService, PageTitleService,
OpenLPService, OpenLPService,
TranslationService, TranslationService,
SentenceCasePipe,
Title, Title,
TitleCasePipe,
WindowRef WindowRef
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

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

View File

@ -1,5 +1,6 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { TitleCasePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { PageTitleService } from '../../page-title.service'; import { PageTitleService } from '../../page-title.service';
@ -21,12 +22,13 @@ export class AlertComponent {
private pageTitleService: PageTitleService, private pageTitleService: PageTitleService,
private openlpService: OpenLPService, private openlpService: OpenLPService,
private snackBar: MatSnackBar, private snackBar: MatSnackBar,
private titleCasePipe: TitleCasePipe,
private translateService: TranslateService) { private translateService: TranslateService) {
this.translateService.stream('ALERTS').subscribe(res => { this.translateService.stream('ALERTS').subscribe(res => {
this.pageTitleService.changePageTitle(res); this.pageTitleService.changePageTitle(res);
}); });
this.translateService.stream('ALERT_SUBMITTED').subscribe(res => { this.translateService.stream('ALERT_SUBMITTED').subscribe(res => {
this.alertMessage = res; this.alertMessage = this.titleCasePipe.transform(res);
}); });
} }

View File

@ -45,7 +45,7 @@
mat-mini-fab color="" mat-mini-fab color=""
class="back-button" class="back-button"
routerLink="/" routerLink="/"
matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate }}"> matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate | titlecase }}">
<mat-icon>arrow_back</mat-icon> <mat-icon>arrow_back</mat-icon>
</a> </a>
} }
@ -65,7 +65,7 @@
@if (!embedded && activeSlide+1 === currentSlides.length) { @if (!embedded && activeSlide+1 === currentSlides.length) {
<div <div
class="next-service-item" class="next-service-item"
matTooltip="{{ 'NEXT_ITEM' | translate }}"> matTooltip="{{ 'NEXT_ITEM' | translate | titlecase }}">
{{ nextServiceItemTitle }} {{ nextServiceItemTitle }}
</div> </div>
} }

View File

@ -13,7 +13,7 @@
</button> </button>
</mat-grid-tile> </mat-grid-tile>
<mat-grid-tile> <mat-grid-tile>
<div id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate }}</div> <div id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate | titlecase }}</div>
</mat-grid-tile> </mat-grid-tile>
<mat-grid-tile> <mat-grid-tile>
<button <button
@ -26,7 +26,7 @@
</button> </button>
</mat-grid-tile> </mat-grid-tile>
<mat-grid-tile> <mat-grid-tile>
<div id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate }}</div> <div id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate | titlecase }}</div>
</mat-grid-tile> </mat-grid-tile>
<mat-grid-tile> <mat-grid-tile>
<button <button
@ -39,7 +39,7 @@
</button> </button>
</mat-grid-tile> </mat-grid-tile>
<mat-grid-tile> <mat-grid-tile>
<div id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate }}</div> <div id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate | titlecase }}</div>
</mat-grid-tile> </mat-grid-tile>
<mat-grid-tile> <mat-grid-tile>
<button <button
@ -52,7 +52,7 @@
</button> </button>
</mat-grid-tile> </mat-grid-tile>
<mat-grid-tile> <mat-grid-tile>
<div id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate }}</div> <div id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate | titlecase }}</div>
</mat-grid-tile> </mat-grid-tile>
</mat-grid-list> </mat-grid-list>
} }
@ -65,7 +65,7 @@
(click)="setMode(displayMode.Blank)" (click)="setMode(displayMode.Blank)"
[disabled]="display.displayMode === displayMode.Blank"> [disabled]="display.displayMode === displayMode.Blank">
<mat-icon class="small-icon">videocam_off</mat-icon> <mat-icon class="small-icon">videocam_off</mat-icon>
<span id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate }}</span> <span id="caption-blank" class="caption">{{ 'SHOW_BLACK' | translate | titlecase }}</span>
</button> </button>
<button <button
mat-list-item mat-list-item
@ -74,7 +74,7 @@
(click)="setMode(displayMode.Theme)" (click)="setMode(displayMode.Theme)"
[disabled]="display.displayMode === displayMode.Theme"> [disabled]="display.displayMode === displayMode.Theme">
<mat-icon class="small-icon">wallpaper</mat-icon> <mat-icon class="small-icon">wallpaper</mat-icon>
<span id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate }}</span> <span id="caption-theme" class="caption">{{ 'SHOW_BACKGROUND' | translate | titlecase }}</span>
</button> </button>
<button <button
mat-list-item mat-list-item
@ -83,7 +83,7 @@
(click)="setMode(displayMode.Desktop)" (click)="setMode(displayMode.Desktop)"
[disabled]="display.displayMode === displayMode.Desktop"> [disabled]="display.displayMode === displayMode.Desktop">
<mat-icon class="small-icon">desktop_windows</mat-icon> <mat-icon class="small-icon">desktop_windows</mat-icon>
<span id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate }}</span> <span id="caption-desktop" class="caption">{{ 'SHOW_DESKTOP' | translate | titlecase }}</span>
</button> </button>
<button <button
mat-list-item mat-list-item
@ -92,7 +92,7 @@
(click)="setMode(displayMode.Presentation)" (click)="setMode(displayMode.Presentation)"
[disabled]="display.displayMode === displayMode.Presentation"> [disabled]="display.displayMode === displayMode.Presentation">
<mat-icon class="small-icon">videocam</mat-icon> <mat-icon class="small-icon">videocam</mat-icon>
<span id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate }}</span> <span id="caption-presentation" class="caption">{{ 'SHOW_PRESENTATION' | translate | titlecase }}</span>
</button> </button>
</mat-action-list> </mat-action-list>
} }

View File

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

View File

@ -1,6 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { TitleCasePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { OpenLPService } from '../../openlp.service'; import { OpenLPService } from '../../openlp.service';
@ -20,12 +21,13 @@ export class LoginComponent {
private dialogRef: MatDialogRef<LoginComponent>, private dialogRef: MatDialogRef<LoginComponent>,
private openlpService: OpenLPService, private openlpService: OpenLPService,
private snackBar: MatSnackBar, private snackBar: MatSnackBar,
private titleCasePipe: TitleCasePipe,
private translateService: TranslateService) { private translateService: TranslateService) {
this.translateService.stream('LOGIN_SUCCEEDED').subscribe(res => { this.translateService.stream('LOGIN_SUCCEEDED').subscribe(res => {
this.loginSucceededMessage = res; this.loginSucceededMessage = this.titleCasePipe.transform(res);
}); });
this.translateService.stream('LOGIN_FAILED').subscribe(res => { this.translateService.stream('LOGIN_FAILED').subscribe(res => {
this.loginFailedMessage = res; this.loginFailedMessage = this.titleCasePipe.transform(res);
}); });
} }

View File

@ -5,7 +5,7 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
export class Nl2BrPipe implements PipeTransform { export class Nl2BrPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { } constructor(private sanitizer: DomSanitizer) { }
transform(value: string): string|SafeHtml { transform(value: string): string | SafeHtml {
if (!value) { if (!value) {
return 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,4 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { TitleCasePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { OpenLPService } from '../../../openlp.service'; import { OpenLPService } from '../../../openlp.service';
@ -17,6 +18,7 @@ export class SearchOptionsComponent {
constructor( constructor(
private openlpService: OpenLPService, private openlpService: OpenLPService,
private titleCasePipe: TitleCasePipe,
private translateService: TranslateService) { } private translateService: TranslateService) { }
// Used to display search-options for certain plugins // Used to display search-options for certain plugins
@ -24,7 +26,7 @@ export class SearchOptionsComponent {
this.selectedPlugin = plugin; this.selectedPlugin = plugin;
if (this.selectedPlugin === 'bibles') { if (this.selectedPlugin === 'bibles') {
this.translateService.stream('BIBLE_VERSION').subscribe(res => { this.translateService.stream('BIBLE_VERSION').subscribe(res => {
this.searchOptionsTitle = res + ':'; this.searchOptionsTitle = this.titleCasePipe.transform(res) + ':';
}); });
this.getSearchOptions(); this.getSearchOptions();
} }

View File

@ -1,4 +1,4 @@
<h3>{{ 'SEARCH' | translate }}</h3> <h3>{{ 'SEARCH' | translate | titlecase }}</h3>
<form #searchForm="ngForm"> <form #searchForm="ngForm">
<mat-form-field> <mat-form-field>
<mat-select <mat-select
@ -24,7 +24,7 @@
matInput matInput
[(ngModel)]="searchText" [(ngModel)]="searchText"
name="searchText" name="searchText"
placeholder="{{ 'SEARCH_TEXT' | translate }}" placeholder="{{ 'SEARCH_TEXT' | translate | titlecase }}"
required> required>
</mat-form-field> </mat-form-field>
<br> <br>
@ -34,12 +34,12 @@
color="primary" color="primary"
[disabled]="!searchForm.form.valid" [disabled]="!searchForm.form.valid"
(click)="onSubmit()"> (click)="onSubmit()">
{{ 'SEARCH' | translate }} {{ 'SEARCH' | translate | titlecase }}
</button> </button>
</form> </form>
@if (searchResults) { @if (searchResults) {
<div> <div>
<h3>{{ 'SEARCH_RESULTS' | translate }}:</h3> <h3>{{ 'SEARCH_RESULTS' | translate | titlecase }}:</h3>
@if (searchResults.length) { @if (searchResults.length) {
<table> <table>
@for (item of searchResults; track item) { @for (item of searchResults; track item) {
@ -49,14 +49,14 @@
<button <button
mat-button color="primary" mat-button color="primary"
(click)="addToService(item[0])"> (click)="addToService(item[0])">
{{ 'ADD_TO_SERVICE' | translate }} {{ 'ADD_TO_SERVICE' | translate | sentencecase }}
</button> </button>
</td> </td>
<td> <td>
<button <button
mat-button color="accent" mat-button color="accent"
(click)="sendLive(item[0])"> (click)="sendLive(item[0])">
{{ 'SEND_LIVE' | translate }} {{ 'SEND_LIVE' | translate | sentencecase }}
</button> </button>
</td> </td>
</tr> </tr>
@ -65,7 +65,7 @@
} }
@else { @else {
<div> <div>
{{ 'NO_SEARCH_RESULTS' | translate }}... {{ 'NO_SEARCH_RESULTS' | translate | sentencecase }}...
</div> </div>
} }
</div> </div>

View File

@ -14,7 +14,7 @@
<div class="no-items"> <div class="no-items">
<div class="no-items-title"> <div class="no-items-title">
<span class="material-icons icon">info</span> <span class="material-icons icon">info</span>
{{ 'NO_SERVICE_ITEMS' | translate }}. {{ 'NO_SERVICE_ITEMS' | translate | sentencecase }}.
</div> </div>
<div class="no-items-actions"> <div class="no-items-actions">
<a <a
@ -22,7 +22,7 @@
mat-stroked-button color="primary" mat-stroked-button color="primary"
size="small"> size="small">
<span class="material-icons">add</span> <span class="material-icons">add</span>
{{ 'ADD_ITEM' | translate }} {{ 'ADD_ITEM' | translate | sentencecase }}
</a> </a>
</div> </div>
</div> </div>

View File

@ -7,7 +7,7 @@ import { ServiceItem } from '../../../responses';
@Component({ @Component({
selector: 'openlp-service-list', selector: 'openlp-service-list',
templateUrl: './service-list.component.html', 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 { export class ServiceListComponent implements OnInit, OnDestroy {

View File

@ -1,7 +1,7 @@
<div class="settings-panel"> <div class="settings-panel">
<mat-card> <mat-card>
<mat-card-header> <mat-card-header>
{{ 'USER_INTERFACE' | translate }} {{ 'USER_INTERFACE' | translate | titlecase }}
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<div class="settings-item"> <div class="settings-item">
@ -9,7 +9,7 @@
color="primary" color="primary"
[checked]="settings.fastSwitching" [checked]="settings.fastSwitching"
(change)="setSetting('fastSwitching', $event.checked)"> (change)="setSetting('fastSwitching', $event.checked)">
{{ 'ENABLE_FAST_SWITCHING_PANEL' | translate }} {{ 'ENABLE_FAST_SWITCHING_PANEL' | translate | sentencecase }}
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
<div class="settings-item"> <div class="settings-item">
@ -17,18 +17,18 @@
color="primary" color="primary"
[checked]="settings.bigDisplayButtons" [checked]="settings.bigDisplayButtons"
(change)="setSetting('bigDisplayButtons', $event.checked)"> (change)="setSetting('bigDisplayButtons', $event.checked)">
{{ 'ENABLE_BIG_DISPLAY_BUTTONS' | translate }} {{ 'ENABLE_BIG_DISPLAY_BUTTONS' | translate | sentencecase }}
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<mat-card> <mat-card>
<mat-card-header> <mat-card-header>
{{ 'STAGE_AND_CHORDS_APPEARANCE' | translate }} {{ 'STAGE_AND_CHORDS_APPEARANCE' | translate | sentencecase }}
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<mat-tab-group> <mat-tab-group>
<mat-tab label="{{ 'STAGE' | translate }}"> <mat-tab label="{{ 'STAGE' | translate | titlecase }}">
<ng-template matTabContent> <ng-template matTabContent>
<ng-container> <ng-container>
<openlp-stage-chord-preview stageType="stage"> <openlp-stage-chord-preview stageType="stage">
@ -38,7 +38,7 @@
</ng-container> </ng-container>
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'CHORDS' | translate }}"> <mat-tab label="{{ 'CHORDS' | translate | titlecase }}">
<ng-template matTabContent> <ng-template matTabContent>
<openlp-stage-chord-preview stageType="chords"> <openlp-stage-chord-preview stageType="chords">
</openlp-stage-chord-preview> </openlp-stage-chord-preview>
@ -55,7 +55,7 @@
let-prefix="prefix"> let-prefix="prefix">
<div class="stage-settings"> <div class="stage-settings">
<div class="settings-item"> <div class="settings-item">
<label>{{ 'FONT_SCALE' | translate }}: {{settings[prefix + 'FontScale'] ?? 100}}%</label> <label>{{ 'FONT_SCALE' | translate | sentencecase }}: {{settings[prefix + 'FontScale'] ?? 100}}%</label>
<mat-slider <mat-slider
min="25" min="25"
max="200" max="200"

View File

@ -15,7 +15,7 @@
<div class="no-items"> <div class="no-items">
<div class="no-items-title"> <div class="no-items-title">
<span class="material-icons icon">info</span> <span class="material-icons icon">info</span>
{{ 'NO_SLIDE_ITEMS' | translate }}. {{ 'NO_SLIDE_ITEMS' | translate | sentencecase }}.
</div> </div>
<div class="no-items-actions"> <div class="no-items-actions">
<a <a
@ -24,7 +24,7 @@
color="primary" color="primary"
size="small"> size="small">
<span class="material-icons">add</span> <span class="material-icons">add</span>
{{ 'ADD_ITEM_TO_SERVICE' | translate }} {{ 'ADD_ITEM_TO_SERVICE' | translate | sentencecase }}
</a> </a>
</div> </div>
</div> </div>

View File

@ -27,7 +27,7 @@
class="back-button" class="back-button"
mat-mini-fab color="" mat-mini-fab color=""
routerLink="/" routerLink="/"
matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate }}"> matTooltip="{{ 'GO_BACK_TO_CONTROLLER' | translate | titlecase }}">
<mat-icon>arrow_back</mat-icon> <mat-icon>arrow_back</mat-icon>
</a> </a>
} }
@ -35,7 +35,7 @@
<button <button
mat-mini-fab mat-mini-fab
class="show-notes" class="show-notes"
matTooltip="{{ 'HIDE_NOTES' | translate }}" matTooltip="{{ 'HIDE_NOTES' | translate | titlecase }}"
color="primary" color="primary"
[class.show-notes-disabled]="false" [class.show-notes-disabled]="false"
(click)="showNotes = false"> (click)="showNotes = false">
@ -46,7 +46,7 @@
<button <button
mat-mini-fab mat-mini-fab
class="show-notes" class="show-notes"
matTooltip="{{ 'SHOW_NOTES' | translate }}" matTooltip="{{ 'SHOW_NOTES' | translate | titlecase }}"
color="" color=""
[class.show-notes-disabled]="true" [class.show-notes-disabled]="true"
(click)="showNotes = true"> (click)="showNotes = true">
@ -56,7 +56,7 @@
@if (!embedded && activeSlide+1 === currentSlides.length) { @if (!embedded && activeSlide+1 === currentSlides.length) {
<div <div
class="next-service-item" class="next-service-item"
matTooltip="{{ 'NEXT_ITEM' | translate }}"> matTooltip="{{ 'NEXT_ITEM' | translate | titlecase }}">
{{ nextServiceItemTitle }} {{ nextServiceItemTitle }}
</div> </div>
} }

View File

@ -1,12 +1,12 @@
<form #themeForm="ngForm"> <form #themeForm="ngForm">
<h4>{{ 'THEME_OPTIONS' | translate }}</h4> <h4>{{ 'THEME_OPTIONS' | translate | titlecase }}</h4>
<div> <div>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'THEME_LEVEL' | translate }}</mat-label> <mat-label>{{ 'THEME_LEVEL' | translate | titlecase }}</mat-label>
<mat-select [(value)]="themeLevel"> <mat-select [(value)]="themeLevel">
<mat-option value="global">{{ 'GLOBAL' | translate }}</mat-option> <mat-option value="global">{{ 'GLOBAL' | translate | titlecase }}</mat-option>
<mat-option value="service">{{ 'SERVICE' | translate }}</mat-option> <mat-option value="service">{{ 'SERVICE' | translate | titlecase }}</mat-option>
<mat-option value="song">{{ 'SONG' | translate }}</mat-option> <mat-option value="song">{{ 'SONG' | translate | titlecase }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@ -29,7 +29,7 @@
} }
@else { @else {
<mat-error> <mat-error>
{{ 'SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED' | translate }} {{ 'SONG_LEVEL_THEME_CHANGING_NOT_SUPPORTED' | translate | sentencecase }}
</mat-error> </mat-error>
} }
</form> </form>

View File

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

987
yarn.lock

File diff suppressed because it is too large Load Diff